package com.zbkj.front.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Maps;
import com.zbkj.dal.base.constants.*;
import com.zbkj.dal.base.enums.*;
import com.zbkj.dal.base.enums.merchant.MerOrderApptType;
import com.zbkj.dal.base.enums.order.RentOrderReturnType;
import com.zbkj.dal.base.page.CommonPage;
import com.zbkj.dal.base.result.CommonResultCode;
import com.zbkj.dal.base.result.IResultEnum;
import com.zbkj.dal.common.enums.ExpressType;
import com.zbkj.dal.common.exception.CrmebException;
import com.zbkj.dal.common.utils.CrmebDateUtil;
import com.zbkj.dal.common.utils.CrmebUtil;
import com.zbkj.dal.common.utils.DateConverter;
import com.zbkj.dal.common.utils.RedisUtil;
import com.zbkj.dal.dao.OrderAddressMapper;
import com.zbkj.dal.entity.cat.Cart;
import com.zbkj.dal.entity.coupon.Coupon;
import com.zbkj.dal.entity.coupon.CouponProduct;
import com.zbkj.dal.entity.coupon.CouponUser;
import com.zbkj.dal.entity.express.ShippingTemplates;
import com.zbkj.dal.entity.express.ShippingTemplatesFree;
import com.zbkj.dal.entity.express.ShippingTemplatesRegion;
import com.zbkj.dal.entity.fund.UserFundAccount;
import com.zbkj.dal.entity.merchant.Merchant;
import com.zbkj.dal.entity.merchant.MerchantOrderAppointment;
import com.zbkj.dal.entity.order.*;
import com.zbkj.dal.entity.product.Product;
import com.zbkj.dal.entity.product.ProductAttrValue;
import com.zbkj.dal.entity.product.ProductCategory;
import com.zbkj.dal.entity.product.ProductReply;
import com.zbkj.dal.entity.seckill.SeckillProduct;
import com.zbkj.dal.entity.user.User;
import com.zbkj.dal.entity.user.UserAddress;
import com.zbkj.dal.entity.user.UserIntegralRecord;
import com.zbkj.dal.entity.wechat.video.PayComponentProduct;
import com.zbkj.dal.entity.wechat.video.PayComponentProductSku;
import com.zbkj.dal.pojo.request.*;
import com.zbkj.dal.pojo.response.*;
import com.zbkj.dal.pojo.vo.*;
import com.zbkj.front.model.req.OrderAddressModifyReq;
import com.zbkj.front.model.req.ReserveRentReturnInStoreModifyTimeReq;
import com.zbkj.front.model.vo.*;
import com.zbkj.front.service.FrontOrderService;
import com.zbkj.front.service.SeckillService;
import com.zbkj.service.manager.*;
import com.zbkj.service.model.dto.OrderRentOverduePaymentRecordCreateDTO;
import com.zbkj.service.model.dto.UseAvailableResultDTO;
import com.zbkj.service.model.dto.address.OrderAddressCreateDTO;
import com.zbkj.service.model.dto.express.CreateExpressOrderDTO;
import com.zbkj.service.model.dto.order.OrderInvoiceDTO;
import com.zbkj.service.model.dto.order.OrderRentRecordCreateDTO;
import com.zbkj.service.model.front.req.order.ReserveRentReturnInStoreReq;
import com.zbkj.service.model.front.req.order.ReserveRentReturnPickUpReq;
import com.zbkj.service.model.front.req.order.LeaseProlongCalculateReq;
import com.zbkj.service.model.front.vo.order.LeaseOrderProlongCalculateVO;
import com.zbkj.service.model.front.vo.order.LeaseOrderProlongCurrentRentVO;
import com.zbkj.service.service.*;
import com.zbkj.service.service.merchant.MerchantOrderAppointmentService;
import com.zbkj.service.service.order.OrderRentRecordService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * H5端订单操作
 * +----------------------------------------------------------------------
 * | CRMEB [ CRMEB赋能开发者，助力企业发展 ]
 * +----------------------------------------------------------------------
 * | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
 * +----------------------------------------------------------------------
 * | Licensed CRMEB并不是自由软件，未经许可不能去掉CRMEB相关版权
 * +----------------------------------------------------------------------
 * | Author: CRMEB Team <admin@crmeb.com>
 * +----------------------------------------------------------------------
 */
@Service
public class FrontOrderServiceImpl implements FrontOrderService {

    private final Logger logger = LoggerFactory.getLogger(FrontOrderServiceImpl.class);

    @Autowired
    private UserService userService;
    @Autowired
    private CartService cartService;
    @Autowired
    private ProductService productService;
    @Autowired
    private ProductAttrValueService productAttrValueService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private OrderInvoiceService orderInvoiceService;
    @Autowired
    private UserAddressService userAddressService;
    @Autowired
    private SystemConfigService systemConfigService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private ShippingTemplatesService shippingTemplatesService;
    @Autowired
    private ShippingTemplatesFreeService shippingTemplatesFreeService;
    @Autowired
    private ShippingTemplatesRegionService shippingTemplatesRegionService;
    @Autowired
    private CouponUserService couponUserService;
    @Autowired
    private CouponProductService couponProductService;
    @Autowired
    private MerchantService merchantService;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Autowired
    private MerchantOrderService merchantOrderService;
    @Autowired
    private OrderDetailService orderDetailService;
    @Autowired
    private OrderStatusService orderStatusService;
    @Autowired
    private SystemAttachmentService systemAttachmentService;
    @Autowired
    private ProductReplyService productReplyService;
    @Autowired
    private RefundOrderService refundOrderService;
    @Autowired
    private RefundOrderInfoService refundOrderInfoService;
    @Autowired
    private PayComponentProductService payComponentProductService;
    @Autowired
    private PayComponentProductSkuService payComponentProductSkuService;
    @Autowired
    private SystemGroupDataService systemGroupDataService;
    @Autowired
    private SeckillService seckillService;
    @Autowired
    private SeckillProductService seckillProductService;
    @Autowired
    private CouponService couponService;
    @Autowired
    private ProductCategoryService productCategoryService;
    @Autowired
    private OrderAddressService orderAddressService;
    @Autowired
    private FundManager fundManager;
    @Autowired
    private OrderManager orderManager;
    @Autowired
    private OrderRentPaymentService orderRentPaymentService;
    @Autowired
    private OrderRentRecordService orderRentRecordService;
    @Autowired
    private MerchantOrderAppointmentService merchantOrderAppointmentService;
    @Autowired
    private QuotaManager quotaManager;
    @Autowired
    private ShareProfitManager shareProfitManager;
    @Resource
    private OrderAddressMapper orderAddressMapper;


    /**
     * 订单预下单
     *
     * @param request 预下单请求参数
     * @return PreOrderResponse
     */
    @Override
    public OrderNoResponse preOrder(PreOrderRequest request) {
        User user = userService.getInfo();
        // 校验预下单商品信息
        PreOrderInfoVo preOrderInfoVo = validatePreOrderRequest(request, user);
        List<PreOrderInfoDetailVo> orderInfoList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : preOrderInfoVo.getMerchantOrderVoList()) {
            orderInfoList.addAll(merchantOrderVo.getOrderInfoList());
            merchantOrderVo.setProTotalFee(merchantOrderVo.getOrderInfoList().stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add));
            merchantOrderVo.setProTotalNum(merchantOrderVo.getOrderInfoList().stream().mapToInt(PreOrderInfoDetailVo::getPayNum).sum());
        }
        // 商品总计金额
        BigDecimal totalPrice = orderInfoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        preOrderInfoVo.setProTotalFee(totalPrice);
        // 购买商品总数量
        int orderProNum = orderInfoList.stream().mapToInt(PreOrderInfoDetailVo::getPayNum).sum();
        preOrderInfoVo.setOrderProNum(orderProNum);
        // 获取默认地址
        UserAddress userAddress = userAddressService.getDefaultByUid(user.getId());
        if (ObjectUtil.isNotNull(userAddress)) {
            // 计算运费
            getFreightFee(preOrderInfoVo, userAddress);
            preOrderInfoVo.setAddressId(userAddress.getId());
        } else {
            preOrderInfoVo.setFreightFee(BigDecimal.ZERO);
            preOrderInfoVo.setAddressId(0);
        }
        // 实际支付金额
        preOrderInfoVo.setPayFee(preOrderInfoVo.getProTotalFee().add(preOrderInfoVo.getFreightFee()));
        preOrderInfoVo.setUserIntegral(user.getIntegral());
        preOrderInfoVo.setUserBalance(user.getNowMoney());
        preOrderInfoVo.setIntegralDeductionSwitch(false);
        preOrderInfoVo.setIsUseIntegral(false);
        String integralDeductionSwitch = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_SWITCH);
        String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
        if ("true".equals(integralDeductionSwitch) && preOrderInfoVo.getProTotalFee().compareTo(new BigDecimal(integralDeductionStartMoney)) >= 0) {
            preOrderInfoVo.setIntegralDeductionSwitch(true);
        }
        // 缓存订单
        String key = user.getId() + CrmebDateUtil.getNowTime().toString() + CrmebUtil.getUuid();
        redisUtil.set(OrderConstants.PRE_ORDER_CACHE_PREFIX + key, JSONObject.toJSONString(preOrderInfoVo), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
        OrderNoResponse response = new OrderNoResponse();
        response.setOrderNo(key);
        response.setOrderType(preOrderInfoVo.getType());
        logger.info("preOrder response:{}", JSON.toJSONString(response));
        return response;
    }

    /**
     * 订单预下单V1.3
     *
     * @param request 预下单请求参数
     * @return PreOrderResponse
     */
    @Override
    public OrderNoResponse preOrder_V1_3(PreOrderRequest request) {
        User user = userService.getInfo();

        //检查用户是否开通角色卡 如果没开通需要弹出开卡弹框
        if (!user.getIsPartnerRole()) {
            throw new CrmebException(CommonResultCode.NOT_ROLE);
        }

        // 校验预下单商品信息
        PreOrderInfoVo preOrderInfoVo = validatePreOrderRequest(request, user);
        List<PreOrderInfoDetailVo> orderInfoList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : preOrderInfoVo.getMerchantOrderVoList()) {
            orderInfoList.addAll(merchantOrderVo.getOrderInfoList());
            merchantOrderVo.setProTotalFee(merchantOrderVo.getOrderInfoList().stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add));
            merchantOrderVo.setProTotalNum(merchantOrderVo.getOrderInfoList().stream().mapToInt(PreOrderInfoDetailVo::getPayNum).sum());
        }
        // 商品总计金额
        BigDecimal totalPrice = orderInfoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (StringUtils.equalsIgnoreCase(request.getPreOrderType(), PreorderConstants.PREORDER_TYPE_rentNow)) {
            totalPrice = orderInfoList.stream().map(e -> e.getRentPrice().multiply(new BigDecimal(e.getPayNum())).multiply(new BigDecimal(e.getRentDays()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        // 共享商品下单时：1、需要检测当前用户的信用额度是否足够（即大于等于商品销售价）。2、限租1件。3、若已存在共享订单未完成归还也不能租
        if (StringUtils.equalsIgnoreCase(request.getPreOrderType(), PreorderConstants.PREORDER_TYPE_rentNow)) {
            Integer uid = userService.getUserIdException();
            request.getOrderDetails().forEach(item -> orderManager.orderPreCheck(uid, item.getProductId()));

            if (totalPrice.compareTo(user.getCreditAmount()) > 0) {
                throw new CrmebException("您的信用额度不足");
            }
            //  TODO HY 暂时关闭承享订单的限制，后续需要打开
//            if (request.getOrderDetails().size() != 1) {
//                throw new CrmebException("只能承享一件商品");
//            }
//            List<Order> userInprogressRentOrders = orderService.getUserInprogressRentOrders(user.getId());
//            if (CollectionUtils.isNotEmpty(userInprogressRentOrders)) {
//                throw new CrmebException("您当前存在未归还的承享商品，暂不能承享");
//            }
        }


        preOrderInfoVo.setProTotalFee(totalPrice);
        // 购买商品总数量
        int orderProNum = orderInfoList.stream().mapToInt(PreOrderInfoDetailVo::getPayNum).sum();
        preOrderInfoVo.setOrderProNum(orderProNum);
        // 获取默认地址
        UserAddress userAddress = userAddressService.getDefaultByUid(user.getId());
        if (ObjectUtil.isNotNull(userAddress)) {
            // 计算运费
//            getFreightFee(preOrderInfoVo, userAddress);
            preOrderInfoVo.setFreightFee(BigDecimal.ZERO);
            preOrderInfoVo.setAddressId(userAddress.getId());
        } else {
            preOrderInfoVo.setFreightFee(BigDecimal.ZERO);
            preOrderInfoVo.setAddressId(0);
        }
        // 实际支付金额
        preOrderInfoVo.setPayFee(preOrderInfoVo.getProTotalFee().add(preOrderInfoVo.getFreightFee()));
        preOrderInfoVo.setUserIntegral(user.getIntegral());
        preOrderInfoVo.setUserBalance(user.getNowMoney());
        preOrderInfoVo.setIntegralDeductionSwitch(false);
        preOrderInfoVo.setIsUseIntegral(false);
        String integralDeductionSwitch = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_SWITCH);
        String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
        if ("true".equals(integralDeductionSwitch) && preOrderInfoVo.getProTotalFee().compareTo(new BigDecimal(integralDeductionStartMoney)) >= 0) {
            preOrderInfoVo.setIntegralDeductionSwitch(true);
        }

        if (request.getPreOrderType().equals("video") || request.getPreOrderType().equals("seckill")) {
            // 活动商品不使用优惠券
            // 缓存订单
            String key = user.getId() + CrmebDateUtil.getNowTime().toString() + CrmebUtil.getUuid();
            redisUtil.set(OrderConstants.PRE_ORDER_CACHE_PREFIX + key, JSONObject.toJSONString(preOrderInfoVo), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
            OrderNoResponse response = new OrderNoResponse();
            response.setOrderNo(key);
            response.setOrderType(preOrderInfoVo.getType());
            logger.info("preOrder response:{}", JSON.toJSONString(response));
            return response;
        }
//        preOrderSetCouponPrice(preOrderInfoVo, orderInfoList, user);
        preOrderInfoVo.setPayFee(preOrderInfoVo.getProTotalFee().add(preOrderInfoVo.getFreightFee()).subtract(preOrderInfoVo.getCouponFee()));

        // 缓存订单
        String key = user.getId() + CrmebDateUtil.getNowTime().toString() + CrmebUtil.getUuid();
        redisUtil.set(OrderConstants.PRE_ORDER_CACHE_PREFIX + key, JSONObject.toJSONString(preOrderInfoVo), OrderConstants.PRE_ORDER_CACHE_TIME, TimeUnit.MINUTES);
        OrderNoResponse response = new OrderNoResponse();
        response.setOrderNo(key);
        response.setOrderType(preOrderInfoVo.getType());
        logger.info("preOrder response:{}", JSON.toJSONString(response));
        return response;
    }

    private void preOrderSetCouponPrice(PreOrderInfoVo preOrderInfoVo, List<PreOrderInfoDetailVo> orderInfoList, User user) {
        // 自动领券计算
        BigDecimal couponPrice = BigDecimal.ZERO;
        List<Integer> proIdsList = orderInfoList.stream().map(PreOrderInfoDetailVo::getProductId).distinct().collect(Collectors.toList());
        List<Integer> merIdList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : preOrderInfoVo.getMerchantOrderVoList()) {
            Integer merId = merchantOrderVo.getMerId();
            merIdList.add(merId);
            List<Integer> proIdList = merchantOrderVo.getOrderInfoList().stream().map(PreOrderInfoDetailVo::getProductId).collect(Collectors.toList());
            BigDecimal merPrice = merchantOrderVo.getProTotalFee();
            List<Coupon> merCouponList = couponService.findManyByMerIdAndMoney(merId, proIdList, merPrice);
            for (int i = 0; i < merCouponList.size(); ) {
                Coupon coupon = merCouponList.get(i);
                if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                    i++;
                    continue;
                }
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList().stream().filter(f -> cpIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proPrice = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(coupon.getMinPrice().toString())) < 0) {
                    merCouponList.remove(i);
                    continue;
                }
                if (proPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    merCouponList.remove(i);
                    continue;
                }
                i++;
            }
            // 查询适用的用户优惠券
            List<CouponUser> merCouponUserList = couponUserService.findManyByUidAndMerIdAndMoneyAndProList(user.getId(), merchantOrderVo.getMerId(), proIdList, merPrice);
            for (int i = 0; i < merCouponUserList.size(); ) {
                CouponUser couponUser = merCouponUserList.get(i);
                if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                    i++;
                    continue;
                }
                Coupon coupon = couponService.getById(couponUser.getCouponId());
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList().stream().filter(f -> cpIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proPrice = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                if (proPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                i++;
            }

            List<Integer> cidList = merCouponUserList.stream().map(CouponUser::getCouponId).collect(Collectors.toList());
            for (int i = 0; i < merCouponList.size(); ) {
                if (cidList.contains(merCouponList.get(i).getId())) {
                    merCouponList.remove(i);
                    continue;
                }
                if (!couponUserService.userIsCanReceiveCoupon(merCouponList.get(i), user.getId())) {
                    merCouponList.remove(i);
                    continue;
                }
                i++;
            }

            if (CollUtil.isEmpty(merCouponList) && CollUtil.isEmpty(merCouponUserList)) {
                continue;
            }
            if (CollUtil.isEmpty(merCouponList)) {
                CouponUser couponUser = merCouponUserList.get(0);
                merchantOrderVo.setUserCouponId(couponUser.getId());
                merchantOrderVo.setCouponFee(new BigDecimal(couponUser.getMoney().toString()));
                merchantOrderVo.setMerCouponFee(new BigDecimal(couponUser.getMoney().toString()));
                setMerProCouponPrice(merchantOrderVo, null, couponUser);
                couponPrice = couponPrice.add(merchantOrderVo.getMerCouponFee());
                continue;
            }
            if (CollUtil.isEmpty(merCouponUserList)) {
                Coupon coupon = merCouponList.get(0);
                Integer couponUserId = couponUserService.autoReceiveCoupon(coupon, user.getId());
                merchantOrderVo.setUserCouponId(couponUserId);
                merchantOrderVo.setCouponFee(new BigDecimal(coupon.getMoney().toString()));
                merchantOrderVo.setMerCouponFee(new BigDecimal(coupon.getMoney().toString()));
                setMerProCouponPrice(merchantOrderVo, coupon, null);
                couponPrice = couponPrice.add(merchantOrderVo.getMerCouponFee());
                continue;
            }

            Coupon coupon = merCouponList.get(0);
            CouponUser couponUser = merCouponUserList.get(0);
            if (couponUser.getMoney() >= coupon.getMoney()) {
                // 使用用户优惠券
                merchantOrderVo.setUserCouponId(merCouponUserList.get(0).getId());
                merchantOrderVo.setCouponFee(new BigDecimal(couponUser.getMoney().toString()));
                merchantOrderVo.setMerCouponFee(new BigDecimal(couponUser.getMoney().toString()));
                setMerProCouponPrice(merchantOrderVo, null, couponUser);
            } else {
                // 领取优惠券下单
                Integer couponUserId = couponUserService.autoReceiveCoupon(merCouponList.get(0), user.getId());
                merchantOrderVo.setUserCouponId(couponUserId);
                merchantOrderVo.setCouponFee(new BigDecimal(coupon.getMoney().toString()));
                merchantOrderVo.setMerCouponFee(new BigDecimal(coupon.getMoney().toString()));
                setMerProCouponPrice(merchantOrderVo, coupon, null);
            }
            couponPrice = couponPrice.add(merchantOrderVo.getMerCouponFee());
        }

        preOrderInfoVo.setMerCouponFee(couponPrice);
        // 自动领平台券计算
        // 查所有适用的平台优惠券
        BigDecimal remainPrice = preOrderInfoVo.getProTotalFee().subtract(preOrderInfoVo.getMerCouponFee());
        List<Product> productList = productService.findByIds(proIdsList);
        List<Integer> proCategoryIdList = productList.stream().map(Product::getCategoryId).collect(Collectors.toList());
        List<Integer> secondParentIdList = productCategoryService.findParentIdByChildIds(proCategoryIdList);
        List<Integer> firstParentIdList = productCategoryService.findParentIdByChildIds(secondParentIdList);
        proCategoryIdList.addAll(secondParentIdList);
        proCategoryIdList.addAll(firstParentIdList);
        List<Integer> brandIdList = productList.stream().map(Product::getBrandId).collect(Collectors.toList());
        List<Coupon> platCouponList = couponService.findManyPlatByMerIdAndMoney(proIdsList, proCategoryIdList, merIdList, brandIdList, preOrderInfoVo.getProTotalFee());
        for (int i = 0; i < platCouponList.size(); ) {
            Coupon coupon = platCouponList.get(i);
            if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_UNIVERSAL)) {
                if (remainPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponList.remove(i);
                    continue;
                }
            }
            if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> cpIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> cpIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(coupon.getMinPrice().toString())) < 0) {
                    platCouponList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponList.remove(i);
                    continue;
                }
            }
            if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT_CATEGORY)) {
                List<Integer> cidList = new ArrayList<>();
                Integer categoryId = Integer.valueOf(coupon.getLinkedData());
                ProductCategory category = productCategoryService.getById(categoryId);
                if (category.getLevel().equals(3)) {
                    cidList.add(categoryId);
                } else {
                    List<ProductCategory> productCategoryList = productCategoryService.findAllChildListByPid(category.getId(), category.getLevel());
                    if (category.getLevel().equals(1)) {
                        productCategoryList = productCategoryList.stream().filter(f -> f.getLevel().equals(3)).collect(Collectors.toList());
                    }
                    cidList.addAll(productCategoryList.stream().map(ProductCategory::getId).collect(Collectors.toList()));
                }
                List<Integer> pIdList = productList.stream().filter(f -> cidList.contains(f.getCategoryId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(coupon.getMinPrice().toString())) < 0) {
                    platCouponList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponList.remove(i);
                    continue;
                }
            }
            if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_BRAND)) {
                Integer brandId = Integer.valueOf(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> brandId.equals(f.getBrandId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(coupon.getMinPrice().toString())) < 0) {
                    platCouponList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponList.remove(i);
                    continue;
                }
            }
            if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_JOINT_MERCHANT)) {
                List<Integer> mpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> mpIdList.contains(f.getMerId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(coupon.getMinPrice().toString())) < 0) {
                    platCouponList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponList.remove(i);
                    continue;
                }
            }
            i++;
        }
        // 查询适用的用户平台优惠券
        List<CouponUser> platCouponUserList = couponUserService.findManyPlatByUidAndMerIdAndMoneyAndProList(user.getId(), proIdsList, proCategoryIdList, merIdList, brandIdList, preOrderInfoVo.getProTotalFee());
        for (int i = 0; i < platCouponUserList.size(); ) {
            CouponUser couponUser = platCouponUserList.get(i);
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_UNIVERSAL)) {
                if (remainPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
            }
            Coupon coupon = couponService.getById(couponUser.getCouponId());
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> cpIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> cpIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT_CATEGORY)) {
                List<Integer> cidList = new ArrayList<>();
                Integer categoryId = Integer.valueOf(coupon.getLinkedData());
                ProductCategory category = productCategoryService.getById(categoryId);
                if (category.getLevel().equals(3)) {
                    cidList.add(categoryId);
                } else {
                    List<ProductCategory> productCategoryList = productCategoryService.findAllChildListByPid(category.getId(), category.getLevel());
                    if (category.getLevel().equals(1)) {
                        productCategoryList = productCategoryList.stream().filter(f -> f.getLevel().equals(3)).collect(Collectors.toList());
                    }
                    cidList.addAll(productCategoryList.stream().map(ProductCategory::getId).collect(Collectors.toList()));
                }
                List<Integer> pIdList = productList.stream().filter(f -> cidList.contains(f.getCategoryId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_BRAND)) {
                Integer brandId = Integer.valueOf(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> brandId.equals(f.getBrandId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_JOINT_MERCHANT)) {
                List<Integer> mpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> mpIdList.contains(f.getMerId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
                if (proSubPrice.compareTo(new BigDecimal(coupon.getMoney().toString())) <= 0) {
                    platCouponUserList.remove(i);
                    continue;
                }
            }
            i++;
        }

        List<Integer> platCidList = platCouponUserList.stream().map(CouponUser::getCouponId).collect(Collectors.toList());
        for (int i = 0; i < platCouponList.size(); ) {
            if (platCidList.contains(platCouponList.get(i).getId())) {
                platCouponList.remove(i);
                continue;
            }
            if (!couponUserService.userIsCanReceiveCoupon(platCouponList.get(i), user.getId())) {
                platCouponList.remove(i);
                continue;
            }
            i++;
        }

        if (CollUtil.isNotEmpty(platCouponUserList) && CollUtil.isEmpty(platCouponList)) {
            // 使用用户优惠券
            preOrderInfoVo.setPlatCouponFee(new BigDecimal(platCouponUserList.get(0).getMoney().toString()));
            preOrderInfoVo.setPlatUserCouponId(platCouponUserList.get(0).getId());
        }
        if (CollUtil.isEmpty(platCouponUserList) && CollUtil.isNotEmpty(platCouponList)) {
            // 领取优惠券下单
            preOrderInfoVo.setPlatCouponFee(new BigDecimal(platCouponList.get(0).getMoney().toString()));
            Integer couponUserId = couponUserService.autoReceiveCoupon(platCouponList.get(0), user.getId());
            preOrderInfoVo.setPlatUserCouponId(couponUserId);
        }
        if (CollUtil.isNotEmpty(platCouponUserList) && CollUtil.isNotEmpty(platCouponList)) {
            Long platCouponMoney = platCouponList.get(0).getMoney();
            Long platCouponUserMoney = platCouponUserList.get(0).getMoney();
            if (platCouponUserMoney >= platCouponMoney) {
                // 使用用户优惠券
                preOrderInfoVo.setPlatCouponFee(new BigDecimal(platCouponUserList.get(0).getMoney().toString()));
                preOrderInfoVo.setPlatUserCouponId(platCouponUserList.get(0).getId());
            } else {
                // 领取优惠券下单
                preOrderInfoVo.setPlatCouponFee(new BigDecimal(platCouponList.get(0).getMoney().toString()));
                Integer couponUserId = couponUserService.autoReceiveCoupon(platCouponList.get(0), user.getId());
                preOrderInfoVo.setPlatUserCouponId(couponUserId);
            }
        }
        preOrderInfoVo.setCouponFee(preOrderInfoVo.getMerCouponFee().add(preOrderInfoVo.getPlatCouponFee()));
    }

    /**
     * 设置订单商品详情商户优惠价格
     */
    private void setMerProCouponPrice(PreMerchantOrderVo merchantOrderVo, Coupon coupon, CouponUser couponUser) {
        if (ObjectUtil.isNull(coupon)) {
            coupon = couponService.getById(couponUser.getCouponId());
        }
        List<PreOrderInfoDetailVo> proDtoList = new ArrayList<>();
        if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
            proDtoList = merchantOrderVo.getOrderInfoList();
        }
        if (coupon.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
            List<Integer> proIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
            proDtoList = merchantOrderVo.getOrderInfoList().stream().filter(d -> proIdList.contains(d.getProductId())).collect(Collectors.toList());
        }
        BigDecimal proTotalPrice = proDtoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal couponPrice = merchantOrderVo.getCouponFee();
        for (int i = 0; i < proDtoList.size(); i++) {
            PreOrderInfoDetailVo d = proDtoList.get(i);
            if (proDtoList.size() == (i + 1)) {
                d.setMerCouponPrice(couponPrice);
                break;
            }
            BigDecimal proPrice = d.getPrice().multiply(new BigDecimal(d.getPayNum().toString()));
            BigDecimal ratio = proPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
            BigDecimal detailCouponFee = couponPrice.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
            couponPrice = couponPrice.subtract(detailCouponFee);
            d.setMerCouponPrice(detailCouponFee);
        }
    }

    /**
     * 加载预下单信息
     *
     * @param preOrderNo 预下单号
     * @return 预下单信息
     */
    @Override
    public PreOrderResponse loadPreOrder(String preOrderNo) {
        UserInfoResponse userInfo = userService.getUserInfo();
        UserFundAccount userFundAccount = fundManager.fundAccount(userInfo.getId(), FundAccountType.CRAB_ROE.name());
        // 通过缓存获取预下单对象
        String key = OrderConstants.PRE_ORDER_CACHE_PREFIX + preOrderNo;
        boolean exists = redisUtil.exists(key);
        if (!exists) {
            throw new CrmebException("预下单订单不存在");
        }
        String orderVoString = redisUtil.get(key);
        PreOrderInfoVo orderInfoVo = JSONObject.parseObject(orderVoString, PreOrderInfoVo.class);
        PreOrderResponse preOrderResponse = new PreOrderResponse();
        BeanUtils.copyProperties(orderInfoVo, preOrderResponse);
        preOrderResponse.setUserIntegral(userFundAccount.getAvailableAmount());
        preOrderResponse.setUserIntegral(userFundAccount.getAvailableAmount());

        List<PreOrderMerchantInfoResponse> infoResponseList = new ArrayList<>();
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        if (orderInfoVo.getType() == 1 || orderInfoVo.getType() == 2) {
            for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
                PreOrderMerchantInfoResponse infoResponse = new PreOrderMerchantInfoResponse();
                BeanUtils.copyProperties(merchantOrderVo, infoResponse);
                infoResponseList.add(infoResponse);
            }
            preOrderResponse.setMerchantInfoList(infoResponseList);
            return preOrderResponse;
        }
        List<Integer> merIdList = new ArrayList<>();
        List<Integer> proIdList = new ArrayList<>();
        List<Product> productList = new ArrayList<>();
        List<PreOrderInfoDetailVo> orderDetailInfoList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            merIdList.add(merchantOrderVo.getMerId());
            PreOrderMerchantInfoResponse infoResponse = new PreOrderMerchantInfoResponse();
            BeanUtils.copyProperties(merchantOrderVo, infoResponse);
            merchantOrderVo.getOrderInfoList().forEach(info -> {
                if (!proIdList.contains(info.getProductId())) {
                    Product product = productService.getById(info.getProductId());
                    productList.add(product);
                    proIdList.add(product.getId());
                }
            });
            orderDetailInfoList.addAll(merchantOrderVo.getOrderInfoList());
            // 查询适用的用户优惠券
            List<Integer> merProIdList = merchantOrderVo.getOrderInfoList().stream().map(PreOrderInfoDetailVo::getProductId).distinct().collect(Collectors.toList());
            BigDecimal merPrice = merchantOrderVo.getProTotalFee();
            BigDecimal merRemainingAmount = merchantOrderVo.getProTotalFee().subtract(orderInfoVo.getPlatCouponFee());
            List<CouponUser> merCouponUserList = couponUserService.findManyByUidAndMerIdAndMoneyAndProList(userInfo.getId(), merchantOrderVo.getMerId(), merProIdList, merPrice);
            for (int i = 0; i < merCouponUserList.size(); ) {
                CouponUser couponUser = merCouponUserList.get(i);
                if (merchantOrderVo.getUserCouponId() > 0 && couponUser.getId().equals(merchantOrderVo.getUserCouponId())) {
                    couponUser.setIsChecked(true);
                    couponUser.setIsChoose(true);
                    i++;
                    continue;
                }
                if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                    if (merRemainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                        couponUser.setIsChoose(true);
                    }
                    i++;
                    continue;
                }
                Coupon coupon = couponService.getById(couponUser.getCouponId());
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList().stream().filter(f -> cpIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proPrice = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proRemainingAmount = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getPlatCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                if (proRemainingAmount.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0) {
                    if (proRemainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                        couponUser.setIsChoose(true);
                    }
                }
                i++;
            }
            infoResponse.setMerCouponUserList(merCouponUserList);
            infoResponseList.add(infoResponse);
        }
        preOrderResponse.setMerchantInfoList(infoResponseList);

        // 获取平台可用优惠券列表
        BigDecimal proTotalFee = orderInfoVo.getProTotalFee();
        List<Integer> proCategoryIdList = productList.stream().map(Product::getCategoryId).collect(Collectors.toList());
        List<Integer> secondParentIdList = productCategoryService.findParentIdByChildIds(proCategoryIdList);
        List<Integer> firstParentIdList = productCategoryService.findParentIdByChildIds(secondParentIdList);
        proCategoryIdList.addAll(secondParentIdList);
        proCategoryIdList.addAll(firstParentIdList);
        List<Integer> brandIdList = productList.stream().map(Product::getBrandId).collect(Collectors.toList());
        List<CouponUser> platCouponUserList = couponUserService.findManyPlatByUidAndMerIdAndMoneyAndProList(userInfo.getId(), proIdList, proCategoryIdList, merIdList, brandIdList, proTotalFee);

        BigDecimal remainingAmount = orderInfoVo.getProTotalFee().subtract(orderInfoVo.getMerCouponFee());
        for (CouponUser couponUser : platCouponUserList) {
            if (orderInfoVo.getPlatUserCouponId() > 0 && couponUser.getId().equals(orderInfoVo.getPlatUserCouponId())) {
                couponUser.setIsChecked(true);
                couponUser.setIsChoose(true);
            }
            // 判断是否可用
            if (remainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_UNIVERSAL)) {
                if (orderInfoVo.getProTotalFee().compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0 && remainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            Coupon coupon = couponService.getById(couponUser.getCouponId());
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> cpIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> cpIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0 && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT_CATEGORY)) {
                List<Integer> cidList = new ArrayList<>();
                Integer categoryId = Integer.valueOf(coupon.getLinkedData());
                ProductCategory category = productCategoryService.getById(categoryId);
                if (category.getLevel().equals(3)) {
                    cidList.add(categoryId);
                } else {
                    List<ProductCategory> productCategoryList = productCategoryService.findAllChildListByPid(category.getId(), category.getLevel());
                    if (category.getLevel().equals(1)) {
                        productCategoryList = productCategoryList.stream().filter(f -> f.getLevel().equals(3)).collect(Collectors.toList());
                    }
                    cidList.addAll(productCategoryList.stream().map(ProductCategory::getId).collect(Collectors.toList()));
                }
                List<Integer> pIdList = productList.stream().filter(f -> cidList.contains(f.getCategoryId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0 && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_BRAND)) {
                Integer brandId = Integer.valueOf(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> brandId.equals(f.getBrandId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0 && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_JOINT_MERCHANT)) {
                List<Integer> mpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> mpIdList.contains(f.getMerId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0 && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
        }

        preOrderResponse.setPlatCouponUserList(platCouponUserList);
        return preOrderResponse;
    }

    /**
     * 计算订单价格
     *
     * @param request 计算订单价格请求对象
     * @return ComputedOrderPriceResponse
     */
    @Override
    public ComputedOrderPriceResponse computedOrderPrice(OrderComputedPriceRequest request) {
        // 通过缓存获取预下单对象
        String key = OrderConstants.PRE_ORDER_CACHE_PREFIX + request.getPreOrderNo();
        boolean exists = redisUtil.exists(key);
        if (!exists) {
            throw new CrmebException("预下单订单不存在");
        }
        List<OrderMerchantRequest> orderMerchantRequestList = request.getOrderMerchantRequestList();
        if (orderMerchantRequestList.stream().anyMatch(e -> e.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP))) {
            orderMerchantRequestList.forEach(m -> {
                if (m.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP)) {
                    Merchant merchant = merchantService.getByIdException(m.getMerId());
                    if (!merchant.getIsTakeTheir()) {
                        throw new CrmebException("请先联系商户管理员开启门店自提");
                    }
                }
            });
        }

        String orderVoString = redisUtil.get(key).toString();
        PreOrderInfoVo orderInfoVo = JSONObject.parseObject(orderVoString, PreOrderInfoVo.class);
        User user = userService.getInfo();

        return computedPrice(request, orderInfoVo, user);
    }

    /**
     * 创建订单
     *
     * @param orderRequest 创建订单请求参数
     * @return OrderNoResponse 订单编号
     */
    @Override
    public OrderNoResponse createOrder(CreateOrderRequest orderRequest) {
        User user = userService.getInfo();
        // 通过缓存获取预下单对象
        String key = OrderConstants.PRE_ORDER_CACHE_PREFIX + orderRequest.getPreOrderNo();
        boolean exists = redisUtil.exists(key);
        if (!exists) {
            throw new CrmebException("预下单订单不存在");
        }
        String orderVoString = redisUtil.get(key).toString();
        PreOrderInfoVo orderInfoVo = JSONObject.parseObject(orderVoString, PreOrderInfoVo.class);
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {
            return seckillService.createOrder(orderRequest, orderInfoVo, user);
        }
        UserAddress userAddress = null;
        List<OrderMerchantRequest> orderMerchantRequestList = orderRequest.getOrderMerchantRequestList();
        if (orderMerchantRequestList.stream().anyMatch(e -> e.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_EXPRESS))) {
            if (ObjectUtil.isNull(orderRequest.getAddressId())) {
                throw new CrmebException("请选择收货地址");
            }
            userAddress = userAddressService.getById(orderRequest.getAddressId());
            if (ObjectUtil.isNull(userAddress) || userAddress.getIsDel()) {
                throw new CrmebException("收货地址有误");
            }
        }
        if (orderMerchantRequestList.stream().anyMatch(e -> e.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP))) {
            orderMerchantRequestList.forEach(m -> {
                if (m.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP)) {
                    Merchant merchant = merchantService.getByIdException(m.getMerId());
                    if (!merchant.getIsTakeTheir()) {
                        throw new CrmebException("请先联系商户管理员开启门店自提");
                    }
                }
            });
        }

        // 校验商品库存
        List<MyRecord> skuRecordList = validateProductStock(orderInfoVo);

        orderInfoVo.getMerchantOrderVoList().forEach(e -> {
            orderMerchantRequestList.forEach(o -> {
                if (o.getMerId().equals(e.getMerId())) {
                    e.setShippingType(o.getShippingType());
                    e.setUserCouponId(o.getUserCouponId());
                    e.setCouponFee(BigDecimal.ZERO);
                    e.setMerCouponFee(BigDecimal.ZERO);
                }
            });
        });
        // 计算订单各种价格
//        getFreightFee(orderInfoVo, userAddress);
        orderInfoVo.setFreightFee(BigDecimal.ZERO);
        orderInfoVo.setPlatUserCouponId(orderRequest.getPlatUserCouponId());
        orderInfoVo.setPlatCouponFee(BigDecimal.ZERO);
        orderInfoVo.setCouponFee(BigDecimal.ZERO);
        orderInfoVo.setMerCouponFee(BigDecimal.ZERO);
        getCouponFee_V1_3(orderInfoVo, user.getId());
        if (orderRequest.getIsUseIntegral()) {// 使用积分
//            integralDeductionComputed(orderInfoVo, user.getIntegral());
            UseAvailableResultDTO useAvailableResultDTO = fundManager.getAvailableAmount(user, orderInfoVo.getPayFee(), FundAccountType.CRAB_ROE.name());
            // 将所有的积分抵扣金额，写入到 orderInfoVo.getMerchantOrderVoList() 集合中
            orderInfoVo.getMerchantOrderVoList().forEach(item -> {
                item.setUseIntegral(1);
                item.setIntegralPrice(useAvailableResultDTO.getRealAmount());
            });
        }
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();

        // 平台订单
        Order order = new Order();
        String orderNo = CrmebUtil.getOrderNo(OrderConstants.ORDER_PREFIX_PLATFORM);
        order.setOrderNo(orderNo);
        order.setPlatOrderNo(orderNo);
        order.setMerId(orderMerchantRequestList.get(0).getMerId());
        order.setUid(user.getId());
        order.setTotalNum(orderInfoVo.getOrderProNum());
        order.setProTotalPrice(orderInfoVo.getProTotalFee());
        order.setTotalPostage(orderInfoVo.getFreightFee());
        order.setTotalPrice(order.getProTotalPrice().add(order.getTotalPostage()));
        order.setCouponPrice(orderInfoVo.getCouponFee());
        order.setUseIntegral(merchantOrderVoList.stream().mapToInt(PreMerchantOrderVo::getUseIntegral).distinct().sum());
        BigDecimal useBalance = merchantOrderVoList.stream().map(PreMerchantOrderVo::getIntegralPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
        if (useBalance.compareTo(order.getTotalPrice()) == 0) {
            order.setIntegralPrice(useBalance.subtract(new BigDecimal("0.01")));
            order.setPayPrice(order.getProTotalPrice().add(order.getTotalPostage()).subtract(order.getCouponPrice()).subtract(order.getIntegralPrice()));
        } else {
            order.setIntegralPrice(useBalance);
            order.setPayPrice(order.getProTotalPrice().add(order.getTotalPostage()).subtract(order.getCouponPrice()).subtract(order.getIntegralPrice()));
        }
        order.setPayPostage(order.getTotalPostage());
        order.setPaid(false);
        order.setCancelStatus(OrderConstants.ORDER_CANCEL_STATUS_NORMAL);
        order.setLevel(OrderConstants.ORDER_LEVEL_PLATFORM);
        order.setType(orderInfoVo.getType());// 默认普通订单
        order.setMerCouponPrice(orderInfoVo.getMerCouponFee());
        order.setPlatCouponPrice(orderInfoVo.getPlatCouponFee());
        order.setPlatCouponId(orderInfoVo.getPlatUserCouponId());
        order.setStatus(OrderType.isPurchase(order.getType()) ? OrderStatusEnums.WAIT_PAY.code() : OrderStatusEnums.RENT_WAIT_PAY.code());

        // 商户订单
        List<Integer> couponIdList = CollUtil.newArrayList();
        if (orderRequest.getPlatUserCouponId() > 0) {
            couponIdList.add(orderRequest.getPlatUserCouponId());
        }
        List<MerchantOrder> merchantOrderList = new ArrayList<>();
        List<OrderDetail> orderDetailList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            MerchantOrder merchantOrder = new MerchantOrder();
            merchantOrder.setOrderNo(CrmebUtil.getOrderNo(OrderConstants.ORDER_PREFIX_MERCHANT));
            merchantOrder.setMerId(merchantOrderVo.getMerId());
            merchantOrder.setUid(user.getId());
            for (OrderMerchantRequest om : orderMerchantRequestList) {
                if (om.getMerId().equals(merchantOrderVo.getMerId())) {
                    if (StrUtil.isNotBlank(om.getRemark())) {
                        merchantOrder.setUserRemark(om.getRemark());
                    }
                    merchantOrder.setShippingType(om.getShippingType());
                    break;
                }
            }
            if (merchantOrder.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP)) {
                merchantOrder.setUserAddress(merchantOrderVo.getMerName());
                merchantOrder.setVerifyCode(String.valueOf(CrmebUtil.randomCount(1111111111, 999999999)));
            } else {
                merchantOrder.setRealName(userAddress.getRealName());
                merchantOrder.setUserPhone(userAddress.getPhone());
                String userAddressStr = userAddress.getProvince() + userAddress.getCity() + userAddress.getDistrict() + userAddress.getStreet() + userAddress.getDetail();
                merchantOrder.setUserAddress(userAddressStr);
            }
            merchantOrder.setTotalNum(merchantOrderVo.getProTotalNum());
            merchantOrder.setProTotalPrice(merchantOrderVo.getProTotalFee());
            merchantOrder.setTotalPostage(merchantOrderVo.getFreightFee());
            merchantOrder.setTotalPrice(merchantOrder.getProTotalPrice().add(merchantOrder.getTotalPostage()));
            merchantOrder.setPayPostage(merchantOrder.getTotalPostage());
            merchantOrder.setUseIntegral(merchantOrderVo.getUseIntegral());
            merchantOrder.setIntegralPrice(merchantOrderVo.getIntegralPrice());
            merchantOrder.setCouponId(merchantOrderVo.getUserCouponId());
            if (merchantOrder.getCouponId() > 0) {
                couponIdList.add(merchantOrder.getCouponId());
            }
            List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList();
            BigDecimal merCouponPrice = detailVoList.stream().map(PreOrderInfoDetailVo::getMerCouponPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            merchantOrder.setMerCouponPrice(merCouponPrice);
            BigDecimal platCouponPrice = detailVoList.stream().map(PreOrderInfoDetailVo::getPlatCouponPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
            merchantOrder.setPlatCouponPrice(platCouponPrice);
            merchantOrder.setCouponPrice(merchantOrder.getMerCouponPrice().add(merchantOrder.getPlatCouponPrice()));
            merchantOrder.setPayPrice(order.getPayPrice());
            merchantOrder.setGainIntegral(0);
            merchantOrder.setType(order.getType());
            merchantOrder.setPlatOrderNo(order.getOrderNo());
            merchantOrderList.add(merchantOrder);

            for (PreOrderInfoDetailVo detailVo : detailVoList) {
                OrderDetail orderDetail = new OrderDetail();
                if (Objects.equals(OrderType.RENT.getCode(), order.getType())) {
                    orderDetail.setPrice(detailVo.getRentPrice());
                } else {
                    orderDetail.setPrice(detailVo.getPrice());
                }
                orderDetail.setPayNum(detailVo.getPayNum());
                orderDetail.setOrderNo(order.getOrderNo());
                orderDetail.setMerId(merchantOrder.getMerId());
                orderDetail.setUid(user.getId());
                orderDetail.setProductId(detailVo.getProductId());
                orderDetail.setProductName(detailVo.getProductName());
                orderDetail.setImage(detailVo.getImage());
                orderDetail.setAttrValueId(detailVo.getAttrValueId());
                orderDetail.setSku(detailVo.getSku());
                orderDetail.setWeight(detailVo.getWeight());
                orderDetail.setVolume(detailVo.getVolume());
                orderDetail.setProductType(detailVo.getProductType());
                orderDetail.setSubBrokerageType(detailVo.getSubBrokerageType());
                orderDetail.setBrokerage(detailVo.getBrokerage());
                orderDetail.setBrokerageTwo(detailVo.getBrokerageTwo());
                orderDetail.setFreightFee(detailVo.getFreightFee());

                orderDetail.setUseIntegral(detailVo.getUseIntegral());
                orderDetail.setIntegralPrice(detailVo.getIntegralPrice());
                orderDetail.setPayPrice(BigDecimal.ZERO);

                orderDetail.setRentDays(detailVo.getRentDays());

                orderDetail.setMerCouponPrice(detailVo.getMerCouponPrice());
                orderDetail.setPlatCouponPrice(detailVo.getPlatCouponPrice());
                orderDetail.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                // 订单只有一个商品，所以订单支付金额就是商品支付金额
                orderDetail.setPayPrice(order.getPayPrice());
//                BigDecimal detailPayPrice = orderDetail.getPrice().multiply(new BigDecimal(orderDetail.getPayNum().toString()));
//                if (order.getType().intValue() == OrderConstants.ORDER_TYPE_RENT) {
//                    detailPayPrice = detailPayPrice.multiply(new BigDecimal(orderDetail.getRentDays().longValue()));
//                }
//                detailPayPrice = detailPayPrice.add(orderDetail.getFreightFee()).subtract(orderDetail.getCouponPrice()).subtract(orderDetail.getIntegralPrice());
//
//                if (detailPayPrice.compareTo(BigDecimal.ZERO) >= 0) {
//                    orderDetail.setPayPrice(detailPayPrice);
//                }
                orderDetailList.add(orderDetail);
            }
        }

        // 创建订单收货地址
        // 1. 获取商户地址
        Optional<OrderMerchantRequest> merchantRequest = orderMerchantRequestList.stream().findFirst();
        if (!merchantRequest.isPresent()) {
            return null;
        }
        OrderDetail orderDetail = orderDetailList.get(0);
        OrderMerchantRequest merchantOptional = merchantRequest.get();
        if (orderMerchantRequestList.stream().anyMatch(e -> e.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_EXPRESS))) {
            Merchant merchant = merchantService.getByIdException(merchantOptional.getMerId());
            // 1. 设置商户地址
            ExpressOrderType orderSubStageType = ExpressOrderType.byOrderType(order.getType());
            orderAddressService.insertAddress(orderNo, OrderAddressType.SHIPPER, orderSubStageType, OrderAddressCreateDTO.buildByMerchantAddress(merchant));
            // 2. 设置用户地址
            orderAddressService.insertAddress(orderNo, OrderAddressType.RECEIVER, orderSubStageType, OrderAddressCreateDTO.buildByUserAddress(userAddress));
        } else if (orderMerchantRequestList.stream().anyMatch(e -> e.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP))) {
            // 创建上门记录
            MerOrderApptType merOrderApptType = OrderType.isPurchase(order.getType()) ? MerOrderApptType.PURCHASE_CREATE : MerOrderApptType.RENT_CREATE;
            merchantOrderAppointmentService.createMerchantOrderAppointmentWithLongTime(orderNo, order.getMerId(), user.getId(), merOrderApptType);
        }

        logger.info("订单生成：商户订单列表： " + JSON.toJSONString(merchantOrderList));
        Boolean execute = transactionTemplate.execute(e -> {
            Boolean result = true;
            logger.info("开始扣减商品库存:order:{}", JSON.toJSONString(order));
            if (order.getType().equals(OrderConstants.ORDER_TYPE_NORMAL) || order.getType().equals(OrderConstants.ORDER_TYPE_RENT)) { // 普通商品或共享商品
                logger.info("开始扣减商品库存 --> 普通商品或共享商品:{}", JSON.toJSONString(skuRecordList));
                // 扣减库存
                for (MyRecord skuRecord : skuRecordList) {
                    // 普通商品口库存
                    result = productService.operationStock(skuRecord.getInt("productId"), skuRecord.getInt("num"), Constants.OPERATION_TYPE_SUBTRACT);
                    if (!result) {
                        e.setRollbackOnly();
                        logger.error("生成订单扣减商品库存失败,预下单号：{},商品ID：{}", orderRequest.getPreOrderNo(), skuRecord.getInt("productId"));
                        return result;
                    }
                    // 普通商品规格扣库存
                    result = productAttrValueService.operationStock(skuRecord.getInt("attrValueId"), skuRecord.getInt("num"), Constants.OPERATION_TYPE_SUBTRACT, ProductConstants.PRODUCT_TYPE_NORMAL, skuRecord.getInt("attrValueVersion"));
                    if (!result) {
                        e.setRollbackOnly();
                        logger.error("生成订单扣减商品sku库存失败,预下单号：{},商品skuID：{}", orderRequest.getPreOrderNo(), skuRecord.getInt("attrValueId"));
                        return result;
                    }
                }
            }
            if (order.getType().equals(OrderConstants.ORDER_TYPE_VIDEO)) {// 视频号订单
                logger.info("开始扣件商品库存 --> 视频号商品:{}", JSON.toJSONString(skuRecordList));
                MyRecord skuRecord = skuRecordList.get(0);
                // 商品规格表扣库存
                productAttrValueService.operationStock(skuRecord.getInt("attrValueId"), skuRecord.getInt("num"), Constants.OPERATION_TYPE_SUBTRACT, ProductConstants.PRODUCT_TYPE_COMPONENT, skuRecord.getInt("attrValueVersion"));
                // 视频商品规格表扣库存
                payComponentProductSkuService.operationStock(skuRecord.getInt("skuId"), skuRecord.getInt("num"), Constants.OPERATION_TYPE_SUBTRACT, skuRecord.getInt("skuVersion"));
                // 视频号商品扣库存
                payComponentProductService.operationStock(skuRecord.getInt("productId"), skuRecord.getInt("num"), Constants.OPERATION_TYPE_SUBTRACT);
            }
            if (order.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {
                logger.info("开始扣件商品库存 --> 秒杀商品:{}", JSON.toJSONString(skuRecordList));
                MyRecord skuRecord = skuRecordList.get(0);
                seckillService.subStock(skuRecord);
            }

            // 扣除用户积分
            if (order.getUseIntegral() > 0 && order.getIntegralPrice().compareTo(BigDecimal.ZERO) > 0) {
                fundManager.useAvailableAmountAndRecordForOrderCreate(user, order.getOrderNo(), order.getIntegralPrice(), orderDetail.getImage());
            }

            orderService.save(order);
            merchantOrderService.saveBatch(merchantOrderList);
            orderDetailService.saveBatch(orderDetailList);

            if (CollUtil.isNotEmpty(couponIdList)) {
                couponUserService.useBatch(couponIdList);
            }

            // 如果是承享订单，需要扣除额度
            if (ObjectUtil.equals(OrderType.RENT.getCode(), order.getType())) {
                // 如果是承享订单，需要创建承享记录
                orderRentRecordService.createOrderRentRecord(OrderRentRecordCreateDTO.buildRecordWhenOrderCreate(orderNo, orderDetail.getProductId(), user.getId(), orderDetail.getRentDays()));
                Product product = productService.findById(orderDetail.getProductId());
                // 如果是承享订单，那么会扣除用户的额度
                quotaManager.useQuota(user.getId(), product.getPrice(), order.getOrderNo(), QuotaChangeType.QUOTA_USE.getCode(), orderDetail.getImage(), QuotaChangeType.QUOTA_USE.getDesc());
            }

            // 生成订单日志
            orderStatusService.createLog(order.getOrderNo(), OrderStatusConstants.ORDER_STATUS_CREATE, OrderStatusConstants.ORDER_LOG_MESSAGE_CREATE);
            // 清除购物车数据
            if (CollUtil.isNotEmpty(orderInfoVo.getCartIdList())) {
                cartService.deleteCartByIds(orderInfoVo.getCartIdList());
            }
            return Boolean.TRUE;
        });
        if (!execute) {
            throw new CrmebException("订单生成失败");
        }

        // 删除缓存订单
        if (redisUtil.exists(key)) {
            redisUtil.delete(key);
        }

        // 加入自动未支付自动取消队列
        redisUtil.lPush(TaskConstants.ORDER_TASK_REDIS_KEY_AUTO_CANCEL_KEY, order.getOrderNo());

        OrderNoResponse response = new OrderNoResponse();
        response.setOrderNo(order.getOrderNo());
        response.setPayPrice(order.getPayPrice());
        return response;
    }

    /**
     * 用户积分记录——订单抵扣
     *
     * @param uid         用户ID
     * @param useIntegral 使用的积分
     * @param integral    用户当前积分
     * @param orderNo     订单号
     * @return 用户积分记录
     */
    private UserIntegralRecord initOrderUseIntegral(Integer uid, Integer useIntegral, Integer integral, String orderNo) {
        UserIntegralRecord integralRecord = new UserIntegralRecord();
        integralRecord.setUid(uid);
        integralRecord.setLinkId(orderNo);
        integralRecord.setLinkType(IntegralRecordConstants.INTEGRAL_RECORD_LINK_TYPE_ORDER);
        integralRecord.setType(IntegralRecordConstants.INTEGRAL_RECORD_TYPE_SUB);
        integralRecord.setTitle(StrUtil.format("订单使用{}积分进行金额抵扣", useIntegral));
        integralRecord.setIntegral(useIntegral);
        integralRecord.setBalance(integral - useIntegral);
        integralRecord.setStatus(IntegralRecordConstants.INTEGRAL_RECORD_STATUS_COMPLETE);
        return integralRecord;
    }

    /**
     * 积分抵扣计算
     *
     * @param orderInfoVo  订单vo
     * @param userIntegral 用户积分
     */
    @Override
    public void integralDeductionComputed(PreOrderInfoVo orderInfoVo, Integer userIntegral) {
        BigDecimal payPrice = orderInfoVo.getProTotalFee().subtract(orderInfoVo.getCouponFee());
        String integralDeductionSwitch = systemConfigService.getValueByKeyException(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_SWITCH);
        if (integralDeductionSwitch.equals("false")) {
            throw new CrmebException("积分抵扣未开启，请重新下单");
        }
        String integralDeductionStartMoney = systemConfigService.getValueByKeyException(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
        if (Integer.parseInt(integralDeductionStartMoney) <= 0 || payPrice.compareTo(new BigDecimal(integralDeductionStartMoney)) < 0) {
            throw new CrmebException("支付金额不满足积分抵扣起始金额，请重新下单");
        }
        // 查询积分使用比例
        String integralRatio = systemConfigService.getValueByKeyException(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_RATIO);
        // 可抵扣金额
        BigDecimal canDeductionPrice = payPrice.multiply(new BigDecimal(integralRatio)).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
        // 积分转换金额
        String integralDeductionMoney = systemConfigService.getValueByKeyException(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_MONEY);
        BigDecimal deductionPrice = new BigDecimal(userIntegral.toString()).multiply(new BigDecimal(integralDeductionMoney));
        // 积分兑换金额小于实际支付可抵扣金额
        int useIntegral;
        BigDecimal integralDeductionPrice;
        if (deductionPrice.compareTo(canDeductionPrice) <= 0) {
            useIntegral = userIntegral;
            integralDeductionPrice = deductionPrice;
        } else {
            useIntegral = canDeductionPrice.divide(new BigDecimal(integralDeductionMoney), 0, BigDecimal.ROUND_UP).intValue();
            integralDeductionPrice = canDeductionPrice;
        }
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        Integer tempUseIntegral = useIntegral;
        BigDecimal tempIntegralDeductionPrice = integralDeductionPrice;
        List<PreMerchantOrderVo> voList = merchantOrderVoList.stream().sorted(Comparator.comparing(o -> (o.getProTotalFee().subtract(o.getCouponFee())))).collect(Collectors.toList());
        BigDecimal paySubPrice = orderInfoVo.getProTotalFee().subtract(orderInfoVo.getMerCouponFee());
        for (int i = 0; i < voList.size(); i++) {
            PreMerchantOrderVo merchantOrderVo = voList.get(i);
            BigDecimal merPayPrice = merchantOrderVo.getProTotalFee().subtract(merchantOrderVo.getMerCouponFee());
            if (merchantOrderVoList.size() == (i + 1)) {
                merchantOrderVo.setUseIntegral(tempUseIntegral);
                merchantOrderVo.setIntegralPrice(tempIntegralDeductionPrice);
                Integer merUseIntegral = tempUseIntegral;
                BigDecimal merIntegralDeductionPrice = tempIntegralDeductionPrice;
                List<PreOrderInfoDetailVo> orderInfoList = merchantOrderVo.getOrderInfoList();
                if (orderInfoList.size() == 1) {
                    orderInfoList.get(0).setUseIntegral(merUseIntegral);
                    orderInfoList.get(0).setIntegralPrice(merIntegralDeductionPrice);
                } else {
                    for (int j = 0; j < orderInfoList.size(); j++) {
                        PreOrderInfoDetailVo detailVo = orderInfoList.get(j);
                        if (orderInfoList.size() == (j + 1)) {
                            detailVo.setUseIntegral(merUseIntegral);
                            detailVo.setIntegralPrice(merIntegralDeductionPrice);
                            break;
                        }
                        BigDecimal detailPayPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum())).subtract(detailVo.getMerCouponPrice());
                        BigDecimal ratio = detailPayPrice.divide(merPayPrice, 10, BigDecimal.ROUND_HALF_UP);
                        int detailUseIntegral = new BigDecimal(merUseIntegral.toString()).multiply(ratio).setScale(0, BigDecimal.ROUND_DOWN).intValue();
                        BigDecimal detailIntegralPrice = new BigDecimal(detailUseIntegral).multiply(new BigDecimal(integralDeductionMoney));
                        merUseIntegral = merUseIntegral - detailUseIntegral;
                        merIntegralDeductionPrice = merIntegralDeductionPrice.subtract(detailIntegralPrice);
                        detailVo.setUseIntegral(detailUseIntegral);
                        detailVo.setIntegralPrice(detailIntegralPrice);
                    }
                }
                break;
            }
            BigDecimal payRatio = merPayPrice.divide(paySubPrice, 10, BigDecimal.ROUND_HALF_UP);
            Integer merUseIntegral = new BigDecimal(tempUseIntegral.toString()).multiply(payRatio).setScale(0, BigDecimal.ROUND_DOWN).intValue();
            if (merUseIntegral.equals(0)) {
                continue;
            }
            BigDecimal merIntegralDeductionPrice = new BigDecimal(merUseIntegral).multiply(new BigDecimal(integralDeductionMoney));
            merchantOrderVo.setUseIntegral(merUseIntegral > tempUseIntegral ? tempUseIntegral : merUseIntegral);
            merchantOrderVo.setIntegralPrice(merIntegralDeductionPrice.compareTo(tempIntegralDeductionPrice) > 0 ? tempIntegralDeductionPrice : merIntegralDeductionPrice);
            if (merIntegralDeductionPrice.compareTo(merPayPrice) > 0) {
                merchantOrderVo.setIntegralPrice(merPayPrice);
            }
            List<PreOrderInfoDetailVo> orderInfoList = merchantOrderVo.getOrderInfoList();
            if (orderInfoList.size() == 1) {
                orderInfoList.get(0).setUseIntegral(merUseIntegral);
                orderInfoList.get(0).setIntegralPrice(merIntegralDeductionPrice);
            } else {
                for (int j = 0; j < orderInfoList.size(); j++) {
                    PreOrderInfoDetailVo detailVo = orderInfoList.get(j);
                    if (orderInfoList.size() == (j + 1)) {
                        detailVo.setUseIntegral(merUseIntegral);
                        detailVo.setIntegralPrice(merIntegralDeductionPrice);
                        break;
                    }
                    BigDecimal detailPayPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum())).subtract(detailVo.getMerCouponPrice());
                    BigDecimal ratio = detailPayPrice.divide(merPayPrice, 10, BigDecimal.ROUND_HALF_UP);
                    int detailUseIntegral = new BigDecimal(merUseIntegral.toString()).multiply(ratio).setScale(0, BigDecimal.ROUND_DOWN).intValue();
                    BigDecimal detailIntegralPrice = new BigDecimal(detailUseIntegral).multiply(new BigDecimal(integralDeductionMoney));
                    merUseIntegral = merUseIntegral - detailUseIntegral;
                    merIntegralDeductionPrice = merIntegralDeductionPrice.subtract(detailIntegralPrice);
                    detailVo.setUseIntegral(detailUseIntegral);
                    detailVo.setIntegralPrice(detailIntegralPrice);
                }
            }
            tempUseIntegral = tempUseIntegral - merchantOrderVo.getUseIntegral();
            tempIntegralDeductionPrice = tempIntegralDeductionPrice.subtract(merchantOrderVo.getIntegralPrice());
        }
        orderInfoVo.setMerchantOrderVoList(voList);
    }

    /**
     * 订单列表
     *
     * @param status      订单状态|-1=全部,0=待支付,1=待发货,2=待收货
     * @param pageRequest 分页参数
     * @return PageInfo
     */
    @Override
    public PageInfo<OrderFrontDataResponse> list(Integer status, Integer type, PageParamRequest pageRequest) {
        Integer userId = userService.getUserIdException();
        return list(userId, status, type, pageRequest);
    }

    @Override
    public PageInfo<OrderFrontDataResponse> list(Integer userId, Integer status, Integer type, PageParamRequest pageRequest) {
        PageInfo<Order> pageInfo = orderService.getUserOrderList(userId, status, type, pageRequest);
        List<Order> orderList = pageInfo.getList();
        if (CollUtil.isEmpty(orderList)) {
            return CommonPage.copyPageInfo(pageInfo, CollUtil.newArrayList());
        }
        List<Integer> merIdList = orderList.stream().map(Order::getMerId).filter(i -> i > 0).distinct().collect(Collectors.toList());
        Map<Integer, Merchant> merchantMap = null;
        if (CollUtil.isNotEmpty(merIdList)) {
            merchantMap = merchantService.getMerIdMapByIdList(merIdList);
        }
        // 组装订单详情
        List<OrderFrontDataResponse> responseList = CollUtil.newArrayList();
        for (Order order : orderList) {
            OrderFrontDataResponse infoResponse = new OrderFrontDataResponse();
            BeanUtils.copyProperties(order, infoResponse);
            //
            boolean canRefund = Objects.equals(infoResponse.getStatus(), OrderStatusEnums.WAIT_SHIPPING.getCode());
            infoResponse.setCanRefund(canRefund ? BooleanEnums.TRUE.getCode() : BooleanEnums.FALSE.getCode());
            // 订单详情对象列表
            List<OrderDetail> orderDetailList = orderDetailService.getByOrderNo(order.getOrderNo());
            List<OrderInfoFrontDataResponse> infoResponseList = CollUtil.newArrayList();
            orderDetailList.forEach(e -> {
                Product product = productService.findById(e.getProductId());
                OrderInfoFrontDataResponse orderInfoResponse = new OrderInfoFrontDataResponse();
                BeanUtils.copyProperties(e, orderInfoResponse);
                orderInfoResponse.setRentPrice(product.getRentPrice());
                infoResponseList.add(orderInfoResponse);
            });
            infoResponse.setOrderInfoList(infoResponseList);
            if (order.getMerId() > 0) {
                infoResponse.setMerName(merchantMap.get(order.getMerId()).getName());
            }

            // 组装当前激活的承享信息
            if (Objects.equals(order.getType(), OrderType.RENT.getCode())) {
                OrderRentSharedRecord rentRecord = orderRentRecordService.getActiveRentRecordByOrderNo(order.getOrderNo());
                if (ObjectUtil.isNotNull(rentRecord)) {
                    OrderFrontDataResponse.OrderFrontRentRecord orderFrontRentRecord = new OrderFrontDataResponse.OrderFrontRentRecord();
                    BeanUtils.copyProperties(rentRecord, orderFrontRentRecord);
                    if (Objects.isNull(rentRecord.getRentEndDate())) {
                        orderFrontRentRecord.setRemainingDays(Long.valueOf(rentRecord.getRentDays()));
                    } else {
                        orderFrontRentRecord.setRemainingDays(Duration.between(LocalDateTime.now(), rentRecord.getRentEndDate()).toDays());
                    }
                    infoResponse.setCurrentRent(orderFrontRentRecord);
                }
            }
            responseList.add(infoResponse);
        }
        return CommonPage.copyPageInfo(pageInfo, responseList);
    }

    /**
     * 移动端订单详情
     *
     * @param orderNo 订单编号
     * @return OrderFrontDetailResponse
     */
    @Override
    public OrderFrontDetailResponse frontDetail(final String orderNo) {
        User currentUser = userService.getInfo();
        Order order = orderService.getByOrderNo(orderNo);
        if (order.getIsUserDel() || order.getIsMerchantDel() || !order.getUid().equals(currentUser.getId())) {
            throw new CrmebException("订单不存在");
        }
        final OrderType orderType = OrderType.of(order.getType());
        OrderFrontDetailResponse response = new OrderFrontDetailResponse();
        BeanUtils.copyProperties(order, response);
        boolean canRefund = Objects.equals(order.getStatus(), OrderStatusEnums.WAIT_SHIPPING.getCode());
        response.setCanRefund(canRefund ? BooleanEnums.TRUE.getCode() : BooleanEnums.FALSE.getCode());

        // -------------------------------------------- 设置商户信息 --------------------------------------------
        MerchantOrder merchantOrder = merchantOrderService.getOneByPlatOrderNo(orderNo);
        MerchantOrderFrontDetailResponse merDetailResponse = new MerchantOrderFrontDetailResponse();
        BeanUtils.copyProperties(merchantOrder, merDetailResponse);
        Merchant merchant = merchantService.getById(merchantOrder.getMerId());
        merDetailResponse.setMerName(merchant.getName());
        if (merchantOrder.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP)) {
            merDetailResponse.setMerPhone(merchant.getPhone());
            merDetailResponse.setMerProvince(merchant.getProvince());
            merDetailResponse.setMerCity(merchant.getCity());
            merDetailResponse.setMerDistrict(merchant.getDistrict());
            merDetailResponse.setMerAddressDetail(merchant.getAddressDetail());
            merDetailResponse.setMerLatitude(merchant.getLatitude());
            merDetailResponse.setMerLongitude(merchant.getLongitude());
        }
//        response.setMerchantOrderResponse(merDetailResponse);
        response.setMerchantOrderList(Collections.singletonList(merDetailResponse));
        response.setDeliverType(merchantOrder.getShippingType());
        response.setDeliverTypeDesc(OrderDeliveryType.of(merchantOrder.getShippingType()));

        // -------------------------------------------- 设置商品信息 --------------------------------------------
        final OrderDetail orderDetail = orderDetailService.getOneByOrderNo(orderNo);
        final Product product = productService.getById(orderDetail.getProductId());

        OrderInfoFrontDataResponse orderInfoFrontDataResponse = new OrderInfoFrontDataResponse();
        BeanUtils.copyProperties(orderDetail, orderInfoFrontDataResponse);
        orderInfoFrontDataResponse.setUnitName(product.getUnitName());
        orderInfoFrontDataResponse.setRentPrice(product.getRentPrice());
        merDetailResponse.setOrderInfoList(Collections.singletonList(orderInfoFrontDataResponse));

        // -------------------------------------------- 设置物流信息(仅取最新一条记录) ----------------------------------------------------
        response.setOrderLogisticsResponse(orderInvoiceService.findTraceByOrderNoAndLastTraceOnly(orderNo));

        // -------------------------------------------- 设置订单发货信息(如果是上门自提) ----------------------------------------------------
        if (Objects.equals(OrderDeliveryType.SCRAP.code(), merchantOrder.getShippingType())) {
            MerchantOrderAppointment appointment = merchantOrderAppointmentService.findAppointmentByOrderId(orderNo, MerOrderApptType.PURCHASE_CREATE, MerOrderApptType.RENT_CREATE);
            OrderFrontDetailResponse.InStoreAppt inStoreAppt = new OrderFrontDetailResponse.InStoreAppt();
            inStoreAppt.setName(merchant.getName());
            inStoreAppt.setPhone(merchant.getPhone());
            inStoreAppt.setAddress(merchant.getAddressDetail());
            inStoreAppt.setLatitude(merchant.getLatitude());
            inStoreAppt.setLongitude(merchant.getLongitude());
            inStoreAppt.setReservationNo(appointment.getApptCode());
            inStoreAppt.setSelfCode(appointment.getApptNumber());
            response.setOrderDeliveryInStoreAppt(inStoreAppt);
        }

        // -------------------------------------------- 设置超时费用 --------------------------------------------
        if (OrderType.isRent(order.getType())) {
            List<OrderRentSharedRecord> orderRentSharedRecords = orderRentRecordService.getAllOrderRentRecordByOrderNo(orderNo);
            List<OrderRentPaymentRecord> rentPaymentRecords = orderRentPaymentService.getByOrderNo(orderNo);
            // 超时天数
            Integer overdueDays = orderRentSharedRecords.stream().map(OrderRentSharedRecord::getOverdueDays).reduce(0, Integer::sum);
            // 自动抵扣的雅识
            BigDecimal integralAmount = rentPaymentRecords.stream()
                    .filter(it -> Objects.equals(it.getPayType(), OrderRentPaymentRecord.PayType.BALANCE_DEDUCTION.name()))
                    .map(OrderRentPaymentRecord::getIntegralAmount)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            // 支付的超时金额
            BigDecimal overdueAmount = rentPaymentRecords.stream()
                    .filter(it -> Objects.equals(it.getPayType(), OrderRentPaymentRecord.PayType.MANUAL_PAYMENT.name()))
                    .map(OrderRentPaymentRecord::getOverdueAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            OrderFrontDetailResponse.OverduePaymentVo overduePaymentVo = new OrderFrontDetailResponse.OverduePaymentVo();
            overduePaymentVo.setOverdueDays(overdueDays);
            overduePaymentVo.setIntegralAmount(integralAmount);
            overduePaymentVo.setOverdueAmount(overdueAmount);
            response.setOverduePaymentVo(overduePaymentVo);
        }

        // -------------------------------------------- 设置各时间阶段 --------------------------------------------
        List<OrderStatus> orderStatuses = orderStatusService.listDeliveryByOrder(orderNo);
        Map<String, LocalDateTime> statusMap = orderStatuses.stream().collect(Collectors.toMap(OrderStatus::getChangeType, OrderStatus::getCreateTime, (o1, o2) -> o2));
        if (CollectionUtils.isNotEmpty(orderStatuses)) {
            OrderFrontDetailResponse.StepsTime stepsTime = new OrderFrontDetailResponse.StepsTime();
            stepsTime.setCreateTime(statusMap.get(OrderStatusChangeType.ORDER_STATUS_CREATE.getCode()));
            stepsTime.setPayTime(statusMap.get(OrderStatusChangeType.ORDER_STATUS_PAY_SUCCESS.getCode()));
            stepsTime.setDeliveryTime(statusMap.get(OrderStatusChangeType.ORDER_STATUS_USER_TAKE_DELIVERY.getCode()));
            stepsTime.setReturnTime(statusMap.get(OrderStatusChangeType.ORDER_STATUS_RENT_RETURN.getCode()));
            stepsTime.setCancelTime(statusMap.get(OrderStatusChangeType.ORDER_STATUS_CANCEL.getCode()));
            response.setStepsTime(stepsTime);
        }

        // -------------------------------------------- 设置承享归还预约信息 ----------------------------------------------------
        // 如果订单是在「待归还」状态中，则还需要补充归还的预约信息
        if (Objects.equals(OrderStatusEnums.RENT_WAIT_RETURN.getCode(), order.getStatus())) {
            OrderFrontDetailResponse.RentReturn rentReturn = new OrderFrontDetailResponse.RentReturn();
            rentReturn.setRentReturnType(order.getRentReturnType());
            // 到店
            if (Objects.equals(RentOrderReturnType.STORE.getCode(), order.getRentReturnType())) {
                MerchantOrderAppointment appointment = merchantOrderAppointmentService.findAppointmentByOrderId(orderNo);
                OrderFrontDetailResponse.InStoreAppt inStoreAppt = new OrderFrontDetailResponse.InStoreAppt();
                inStoreAppt.setName(merchant.getName());
                inStoreAppt.setPhone(merchant.getPhone());
                inStoreAppt.setAddress(merchant.getAddressDetail());
                inStoreAppt.setLatitude(merchant.getLatitude());
                inStoreAppt.setLongitude(merchant.getLongitude());
                inStoreAppt.setReservationNo(appointment.getApptCode());
                inStoreAppt.setDateDesc(DateConverter.convertDate(appointment.getApptDate()));
                inStoreAppt.setStartTime(appointment.getApptTimeStart());
                inStoreAppt.setEndTime(appointment.getApptTimeEnd());
                inStoreAppt.setSelfCode(appointment.getApptNumber());
                rentReturn.setInStoreAppt(inStoreAppt);
            }
            // 上门
            if (Objects.equals(RentOrderReturnType.PICKUP.getCode(), order.getRentReturnType())) {
                // 物流信息
                OrderInvoiceDTO invoice = orderService.findOrderInvoiceByOrderNoAndInvoiceType(orderNo, ExpressOrderType.RENT_RETURN);
                // 订单地址信息
                OrderAddress address = orderAddressService.getOneOrderAddressList(orderNo, OrderAddressType.SHIPPER, ExpressOrderType.RENT_RETURN);
                OrderFrontDetailResponse.PickUpAppt pickUpAppt = new OrderFrontDetailResponse.PickUpAppt();
                pickUpAppt.setDeliveryName(invoice.getExpressName());
                pickUpAppt.setPickupCode(invoice.getPickupCode());
                pickUpAppt.setSenderName(address.getContactName());
                pickUpAppt.setSenderPhone(address.getContactPhone());
                pickUpAppt.setSenderAddress(address.buildAddressDetail());
                LocalDateTime pickupTime = invoice.getPickupTime();
                pickUpAppt.setDateDesc(DateConverter.convertDate(pickupTime.toLocalDate()));
                pickUpAppt.setStartTime(pickupTime.toLocalTime());
                pickUpAppt.setEndTime(pickupTime.toLocalTime().plusHours(2));
                rentReturn.setPickUpAppt(pickUpAppt);
            }
            response.setRentReturn(rentReturn);
        }

        // -------------------------------------------- 设置日志时间相关信息 ----------------------------------------------------
        OrderFrontDetailResponse.OrderStatusExt orderStatusExt = new OrderFrontDetailResponse.OrderStatusExt();
        if (OrderType.isPurchase(order.getType())) {
            // 待支付
            if (Objects.equals(OrderStatusEnums.WAIT_PAY.getCode(), order.getStatus())) {
                orderStatusExt.setType(OrderFrontDetailResponse.TimeType.AUTO_CLOSE);
                orderStatusExt.setTime1(order.getCreateTime().plusMinutes(15));
            }
        } else if (OrderType.isRent(order.getType())) {
            // 待支付
            if (Objects.equals(OrderStatusEnums.WAIT_PAY.getCode(), order.getStatus())) {
                orderStatusExt.setType(OrderFrontDetailResponse.TimeType.AUTO_CLOSE);
                orderStatusExt.setTime1(order.getCreateTime().plusMinutes(15));
            }
            // 用户待收货
            else if (Objects.equals(OrderStatusEnums.RENT_WAIT_RECEIPT.getCode(), order.getStatus())) {
                OrderStatus orderStatus = orderStatusService.selectOneByOrderNoAndChangeType(orderNo, OrderStatusChangeType.ORDER_STATUS_EXPRESS.getCode());
                orderStatusExt.setType(OrderFrontDetailResponse.TimeType.PLAT_DELIVERY);
                orderStatusExt.setTime1(orderStatus.getCreateTime());
            }
            // 用户待确认收货
            else if (Objects.equals(OrderStatusEnums.RENT_AWAIT_DELIVERY.getCode(), order.getStatus())) {
                OrderStatus orderStatus = orderStatusService.selectOneByOrderNoAndChangeType(orderNo, OrderStatusChangeType.ORDER_STATUS_SIGN.getCode());
                orderStatusExt.setType(OrderFrontDetailResponse.TimeType.AUTO_RECEIPT);
                orderStatusExt.setTime1(orderStatus.getCreateTime().minusHours(24));
            }
            // 商品承享中
            else if (Objects.equals(OrderStatusEnums.RENT_PROGRESS.getCode(), order.getStatus())) {
                OrderRentSharedRecord rentSharedRecord = orderRentRecordService.getActiveRentRecordByOrderNo(orderNo);
                orderStatusExt.setType(OrderFrontDetailResponse.TimeType.RENTING);
                orderStatusExt.setTime1(rentSharedRecord.getRentStartDate());
                orderStatusExt.setTime2(rentSharedRecord.getRentEndDate());
                orderStatusExt.setRemainingDays(Duration.between(LocalDateTime.now(), rentSharedRecord.getRentEndDate()).toDays());
            }
            // 待贝贝奢护收货
            else if (Objects.equals(OrderStatusEnums.RENT_WAIT_RECEIVE.getCode(), order.getStatus())) {
                OrderStatus orderStatus = orderStatusService.selectOneByOrderNoAndChangeType(orderNo, OrderStatusChangeType.ORDER_STATUS_USER_RETURN.getCode());
                orderStatusExt.setType(OrderFrontDetailResponse.TimeType.USER_RETURN_DELIVERY);
                orderStatusExt.setTime1(orderStatus.getCreateTime());
            }
            // 贝贝奢护验收中
            else if (Objects.equals(OrderStatusEnums.RENT_SCRAPY_VERIFY.getCode(), order.getStatus())) {
                OrderStatus orderStatus = orderStatusService.selectOneByOrderNoAndChangeType(orderNo, OrderStatusChangeType.ORDER_STATUS_USER_RETURN_PLAT_SIGN.getCode());
                orderStatusExt.setType(OrderFrontDetailResponse.TimeType.PLANT_RETURN_RECEIPT);
                orderStatusExt.setTime1(orderStatus.getCreateTime());
            }
            // 承享已经超时
            else if (Objects.equals(OrderStatusEnums.RENT_OVERDUE.getCode(), order.getStatus())) {
                OrderRentSharedRecord rentSharedRecord = orderRentRecordService.getActiveRentRecordByOrderNo(orderNo);
                orderStatusExt.setType(OrderFrontDetailResponse.TimeType.RENTING);
                orderStatusExt.setTime1(rentSharedRecord.getExpiredEndDate());
                orderStatusExt.setTime2(rentSharedRecord.getExpiredEndDate().plusSeconds(1));
            }
        }
        response.setOrderStatusExt(orderStatusExt);
        return response;
    }

    /**
     * 订单商品评论列表
     *
     * @param pageRequest 分页参数
     * @return PageInfo
     */
    @Override
    public PageInfo<InfoReplyResponse> replyList(PageParamRequest pageRequest) {
        Integer userId = userService.getUserIdException();
        PageInfo<OrderDetail> pageInfo = orderDetailService.getReplyList(userId, false, pageRequest);
        List<OrderDetail> orderDetailList = pageInfo.getList();
        if (CollUtil.isEmpty(orderDetailList)) {
            return CommonPage.copyPageInfo(pageInfo, CollUtil.newArrayList());
        }
        List<Integer> merIdList = orderDetailList.stream().map(OrderDetail::getMerId).distinct().collect(Collectors.toList());
        Map<Integer, Merchant> merchantMap = merchantService.getMerIdMapByIdList(merIdList);
        List<InfoReplyResponse> responseList = orderDetailList.stream().map(info -> {
            InfoReplyResponse replyResponse = new InfoReplyResponse();
            BeanUtils.copyProperties(info, replyResponse);
            replyResponse.setMerName(merchantMap.get(info.getMerId()).getName());
            return replyResponse;
        }).collect(Collectors.toList());
        return CommonPage.copyPageInfo(pageInfo, responseList);
    }

    /**
     * 评价订单商品
     *
     * @param request 评价参数
     */
    @Override
    public Boolean replyProduct(OrderProductReplyRequest request) {
        Order order = orderService.getByOrderNo(request.getOrderNo());
        if (!(order.getStatus().equals(OrderConstants.ORDER_STATUS_COMPLETE) || order.getStatus().equals(OrderConstants.ORDER_STATUS_TAKE_DELIVERY))) {
            throw new CrmebException("订单状态异常，无法评价");
        }
        OrderDetail orderDetail = orderDetailService.getById(request.getOrderDetailId());
        if (ObjectUtil.isNull(orderDetail) || !order.getOrderNo().equals(orderDetail.getOrderNo())) {
            throw new CrmebException("订单详情不存在");
        }
        if (!orderDetail.getIsReceipt()) {
            throw new CrmebException("请收货后再评论");
        }
        if (orderDetail.getIsReply()) {
            throw new CrmebException("已评价!");
        }
        orderDetail.setIsReply(true);
        User user = userService.getInfo();
        ProductReply productReply = new ProductReply();
        BeanUtils.copyProperties(request, productReply);
        productReply.setMerId(orderDetail.getMerId());
        productReply.setProductId(orderDetail.getProductId());
        if (order.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {
            SeckillProduct seckillProduct = seckillProductService.getById(orderDetail.getProductId());
            productReply.setProductId(seckillProduct.getProductId());
        }
        productReply.setAttrValueId(orderDetail.getAttrValueId());
        productReply.setSku(orderDetail.getSku());
        productReply.setUid(user.getId());
        productReply.setNickname(user.getNickname());
        String cdnUrl = systemAttachmentService.getCdnUrl();
        productReply.setAvatar(systemAttachmentService.clearPrefix(user.getAvatar(), cdnUrl));
        if (CollUtil.isNotEmpty(request.getPics())) {
            List<String> pics = request.getPics().stream().map(e -> systemAttachmentService.clearPrefix(e, cdnUrl)).collect(Collectors.toList());
            productReply.setPics(String.join(",", pics));
        }
        Boolean execute = transactionTemplate.execute(e -> {
            orderDetailService.updateById(orderDetail);
            productReplyService.save(productReply);
            return Boolean.TRUE;
        });
        if (!execute) {
            throw new CrmebException("评价订单商品失败");
        }
        return execute;
    }

    /**
     * 取消订单
     *
     * @param orderNo 订单编号
     */
    @Override
    public Boolean cancel(String orderNo) {
        Integer uid = userService.getUserIdException();
        Order order = orderService.getByOrderNo(orderNo);
        if (!order.getUid().equals(uid)) {
            throw new CrmebException("订单不存在");
        }
        if (order.getPaid()) {
            throw new CrmebException("订单已支付，无法取消");
        }
        Boolean cancel = orderService.cancel(orderNo, true);
        if (cancel) {
            redisUtil.lPush(TaskConstants.ORDER_TASK_REDIS_KEY_AFTER_CANCEL_BY_USER, order.getOrderNo());
        }
        return cancel;
    }

    /**
     * 订单收货
     *
     * @param orderNo 订单号
     * @return Boolean
     */
    @Override
    public Boolean takeDelivery(String orderNo) {
        Integer userId = userService.getUserIdException();
        Order order = orderService.getByOrderNo(orderNo);
        if (!order.getUid().equals(userId)) {
            throw new CrmebException("订单不存在");
        }
        if (!order.getCancelStatus().equals(OrderConstants.ORDER_CANCEL_STATUS_NORMAL)) {
            throw new CrmebException("订单已取消");
        }
        if (order.getRefundStatus().equals(OrderConstants.ORDER_REFUND_STATUS_ALL)) {
            throw new CrmebException("已退款订单无法收货");
        }
        if (!OrderStatusEnums.canDelivery(order.getStatus())) {
            throw new CrmebException("订单状态异常");
        }
        Boolean execute = transactionTemplate.execute(e -> {
            orderService.takeDelivery(orderNo);
            orderDetailService.takeDelivery(orderNo);
            // 假设是承享订单，需要开始记录承享记录
            if (OrderType.isRent(order.getType())) {
                orderRentRecordService.updateInitialRentOrderStartOnSign(order.getPlatOrderNo());
            }
            // 如果是买入订单，则需要开始确认分润结果
            if (OrderType.isPurchase(order.getType())) {
                shareProfitManager.shareProfitConfirmAsync(order.getPlatOrderNo());
            }
            return Boolean.TRUE;
        });
        if (execute) {
            //后续操作放入redis
            redisUtil.lPush(TaskConstants.ORDER_TASK_REDIS_KEY_AFTER_TAKE_BY_USER, orderNo);
        }
        return execute;
    }

    /**
     * 删除订单
     *
     * @param orderNo 订单号
     * @return Boolean
     */
    @Override
    public Boolean delete(String orderNo) {
        Integer userId = userService.getUserIdException();
        Order order = orderService.getByOrderNo(orderNo);
        if (!order.getUid().equals(userId)) {
            throw new CrmebException("订单不存在");
        }
        if (!(order.getStatus().equals(OrderConstants.ORDER_STATUS_COMPLETE) || order.getStatus().equals(OrderConstants.ORDER_STATUS_CANCEL))) {
            throw new CrmebException("未完成订单无法删除");
        }
        if (order.getIsUserDel()) {
            throw new CrmebException("订单已删除，请不要进行重复操作");
        }
        order.setIsUserDel(true);
        return transactionTemplate.execute(e -> {
            orderService.updateById(order);
            orderStatusService.createLog(orderNo, OrderStatusConstants.ORDER_STATUS_USER_DELETE, OrderStatusConstants.ORDER_LOG_USER_DELETE);
            return Boolean.TRUE;
        });
    }

    /**
     * 售后申请列表(可申请售后列表)
     *
     * @param pageParamRequest 分页参数
     */
    @Override
    public PageInfo<OrderDetail> getAfterSaleApplyList(String orderNo, PageParamRequest pageParamRequest) {
        Integer uid = userService.getUserIdException();
        PageInfo<OrderDetail> pageInfo = orderDetailService.findAfterSaleApplyList(uid, orderNo, pageParamRequest);
        List<OrderDetail> orderDetailList = pageInfo.getList();
        if (CollUtil.isEmpty(orderDetailList)) {
            return pageInfo;
        }
        List<Integer> merIdList = orderDetailList.stream().map(OrderDetail::getMerId).distinct().collect(Collectors.toList());
        Map<Integer, Merchant> merchantMap = merchantService.getMapByIdList(merIdList);
        orderDetailList.forEach(o -> {
            o.setMerName(merchantMap.get(o.getMerId()).getName());
        });
        return pageInfo;
    }

    /**
     * 查询退款理由
     *
     * @return 退款理由集合
     */
    @Override
    public List<String> getRefundReason() {
        String reasonString = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_STOR_REASON);
        reasonString = CrmebUtil.UnicodeToCN(reasonString);
        reasonString = reasonString.replace("rn", "n");
        return Arrays.asList(reasonString.split("\\n"));
    }

    /**
     * 订单退款申请
     *
     * @param request 申请参数
     * @return Boolean
     */
    @Override
    public Boolean refundApply(OrderRefundApplyRequest request) {
        Integer uid = userService.getUserIdException();
        Order order = orderService.getByOrderNo(request.getOrderNo());
        if (!order.getUid().equals(uid) || order.getIsUserDel()) {
            throw new CrmebException("订单不存在");
        }
        if (order.getCancelStatus() > OrderConstants.ORDER_CANCEL_STATUS_NORMAL) {
            throw new CrmebException("订单已取消");
        }
        if (!order.getPaid()) {
            throw new CrmebException("未支付订单无法申请退款");
        }
        if (order.getRefundStatus().equals(OrderConstants.ORDER_REFUND_STATUS_ALL)) {
            throw new CrmebException("订单已全部退款");
        }
        if (order.getStatus().equals(OrderConstants.ORDER_STATUS_COMPLETE)) {
            throw new CrmebException("已完成订单无法申请退款");
        }
        OrderDetail orderDetail = orderDetailService.getById(request.getOrderDetailId());
        if (ObjectUtil.isNull(orderDetail) || !orderDetail.getOrderNo().equals(order.getOrderNo())) {
            throw new CrmebException("订单详情不存在");
        }
        int canApplyNum = orderDetail.getPayNum() - orderDetail.getApplyRefundNum() - orderDetail.getRefundNum();
        if (canApplyNum < request.getNum()) {
            throw new CrmebException(StrUtil.format("剩余可退款数量为{}", canApplyNum));
        }
        MerchantOrder merchantOrder = merchantOrderService.getOneByPlatOrderNo(order.getOrderNo());

        RefundOrder refundOrder = new RefundOrder();
        refundOrder.setRefundOrderNo(CrmebUtil.getOrderNo(OrderConstants.ORDER_PREFIX_REFUND));
        refundOrder.setOrderNo(order.getOrderNo());
        refundOrder.setMerId(order.getMerId());
        refundOrder.setUid(order.getUid());
        refundOrder.setRealName(merchantOrder.getRealName());
        refundOrder.setUserPhone(merchantOrder.getUserPhone());
        refundOrder.setUserAddress(merchantOrder.getUserAddress());
        refundOrder.setRefundPrice(order.getPayPrice());
        refundOrder.setTotalNum(order.getTotalNum());
        refundOrder.setRefundReasonWap(request.getText());
        refundOrder.setRefundReasonWapImg(systemAttachmentService.clearPrefix(request.getReasonImage()));
        refundOrder.setRefundReasonWapExplain(request.getExplain());
        refundOrder.setRefundStatus(OrderConstants.MERCHANT_REFUND_ORDER_STATUS_APPLY);
        refundOrder.setRefundPlatCouponPrice(merchantOrder.getPlatCouponPrice());

        RefundOrderInfo refundOrderInfo = new RefundOrderInfo();
        refundOrderInfo.setRefundOrderNo(refundOrder.getRefundOrderNo());
        refundOrderInfo.setMerId(orderDetail.getMerId());
        refundOrderInfo.setOrderDetailId(orderDetail.getId());
        refundOrderInfo.setProductId(orderDetail.getProductId());
        refundOrderInfo.setProductName(orderDetail.getProductName());
        refundOrderInfo.setImage(orderDetail.getImage());
        refundOrderInfo.setAttrValueId(orderDetail.getAttrValueId());
        refundOrderInfo.setSku(orderDetail.getSku());
        refundOrderInfo.setPrice(orderDetail.getPrice());
        refundOrderInfo.setPayNum(orderDetail.getPayNum());
        refundOrderInfo.setProductType(orderDetail.getProductType());
        refundOrderInfo.setPayPrice(orderDetail.getPayPrice());
        refundOrderInfo.setApplyRefundNum(request.getNum());
        // 临时性计算退款金额、积分
        if (request.getNum().equals(orderDetail.getPayNum())) {
            refundOrderInfo.setRefundPrice(orderDetail.getPayPrice());
            refundOrderInfo.setRefundUseIntegral(orderDetail.getUseIntegral());
            refundOrderInfo.setRefundGainIntegral(orderDetail.getGainIntegral());
            refundOrderInfo.setRefundFreightFee(orderDetail.getFreightFee());
        } else {
            refundOrderInfo.setRefundUseIntegral(0);
            refundOrderInfo.setRefundGainIntegral(0);
            BigDecimal ratio = new BigDecimal(request.getNum().toString()).divide(new BigDecimal(orderDetail.getPayNum().toString()), 10, BigDecimal.ROUND_HALF_UP);
            refundOrderInfo.setRefundPrice(orderDetail.getPayPrice().multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP));
            if (orderDetail.getUseIntegral() > 0) {
                refundOrderInfo.setRefundUseIntegral(new BigDecimal(orderDetail.getUseIntegral().toString()).multiply(ratio).setScale(0, BigDecimal.ROUND_HALF_UP).intValue());
            }
            if (orderDetail.getGainIntegral() > 0) {
                refundOrderInfo.setRefundGainIntegral(new BigDecimal(orderDetail.getGainIntegral().toString()).multiply(ratio).setScale(0, BigDecimal.ROUND_HALF_UP).intValue());
            }
            if (orderDetail.getPlatCouponPrice().compareTo(BigDecimal.ZERO) > 0) {
                refundOrderInfo.setRefundPlatCouponPrice(orderDetail.getPlatCouponPrice().multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP));
            }
            refundOrderInfo.setRefundFreightFee(orderDetail.getFreightFee().multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP));
        }

        refundOrder.setRefundPrice(refundOrderInfo.getRefundPrice());
        refundOrder.setRefundUseIntegral(refundOrderInfo.getRefundUseIntegral());
        refundOrder.setRefundGainIntegral(refundOrderInfo.getRefundGainIntegral());
        refundOrder.setRefundPlatCouponPrice(refundOrderInfo.getRefundPlatCouponPrice());
        refundOrder.setRefundFreightFee(refundOrderInfo.getRefundFreightFee());

        order.setRefundStatus(OrderConstants.ORDER_REFUND_STATUS_APPLY);
        orderDetail.setApplyRefundNum(orderDetail.getApplyRefundNum() + request.getNum());
        Boolean execute = transactionTemplate.execute(e -> {
            orderService.updateById(order);
            orderDetailService.updateById(orderDetail);
            refundOrderService.save(refundOrder);
            refundOrderInfoService.save(refundOrderInfo);
            return Boolean.TRUE;
        });
        if (!execute) {
            throw new CrmebException("申请退款失败");
        }
        return execute;
    }

    /**
     * 退款订单列表
     *
     * @param type        列表类型：0-处理中，9-申请记录
     * @param pageRequest 分页参数
     * @return PageInfo
     */
    @Override
    public PageInfo<RefundOrderResponse> getRefundOrderList(Integer type, PageParamRequest pageRequest) {
        PageInfo<RefundOrder> pageInfo = refundOrderService.getH5List(type, pageRequest);
        List<RefundOrder> refundOrderList = pageInfo.getList();
        if (CollUtil.isEmpty(refundOrderList)) {
            return CommonPage.copyPageInfo(pageInfo, CollUtil.newArrayList());
        }
        List<RefundOrderResponse> responseList = refundOrderList.stream().map(refundOrder -> {
            RefundOrderResponse response = new RefundOrderResponse();
            BeanUtils.copyProperties(refundOrder, response);
            RefundOrderInfo orderInfo = refundOrderInfoService.getByRefundOrderNo(refundOrder.getRefundOrderNo());
            response.setProductName(orderInfo.getProductName());
            response.setImage(orderInfo.getImage());
            response.setSku(orderInfo.getSku());
            response.setApplyRefundNum(orderInfo.getApplyRefundNum());
            return response;
        }).collect(Collectors.toList());
        return CommonPage.copyPageInfo(pageInfo, responseList);
    }

    /**
     * 退款订单详情
     *
     * @param refundOrderNo 退款订单号
     * @return RefundOrderInfoResponse
     */
    @Override
    public RefundOrderInfoResponse refundOrderDetail(String refundOrderNo) {
        return refundOrderService.getRefundOrderDetailByRefundOrderNo(refundOrderNo);
    }

    /**
     * 订单物流详情
     */
    @Override
    public LogisticsResultVo getLogisticsInfo(Integer invoiceId) {
        LogisticsResultVo logisticsInfo = orderService.getLogisticsInfo(invoiceId);
        String orderNo = logisticsInfo.getOrderNo();
        LambdaQueryWrapper<OrderAddress> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderAddress::getOrderNo, orderNo);
        wrapper.eq(OrderAddress::getAddressType, OrderAddressType.RECEIVER.getType());
        wrapper.eq(OrderAddress::getBusinessType, ExpressOrderType.RENT_CREATE.name());
        OrderAddress orderAddress = orderAddressMapper.selectOne(wrapper);
        if (ObjectUtil.isNotNull(orderAddress)) {
            logisticsInfo.setUsername(orderAddress.getContactName());
            if (StringUtils.isNotBlank(orderAddress.getContactPhone())) {
                logisticsInfo.setPhone(DesensitizedUtil.mobilePhone(orderAddress.getContactPhone()));
            }
            logisticsInfo.setProvinceName(orderAddress.getProvinceName());
            logisticsInfo.setCityName(orderAddress.getCityName());
            logisticsInfo.setRegionName(orderAddress.getRegionName());
            logisticsInfo.setTownName(orderAddress.getTownName());
            logisticsInfo.setAddress(orderAddress.getAddress());
        }
        return logisticsInfo;
    }

    /**
     * 获取发货单列表
     *
     * @param orderNo 订单号
     * @return 发货单列表
     */
    @Override
    public OrderInvoiceFrontResponse getInvoiceList(String orderNo) {
        Order order = orderService.getByOrderNo(orderNo);
        List<OrderInvoiceResponse> invoiceList = orderService.getInvoiceList(orderNo);
        OrderInvoiceFrontResponse response = new OrderInvoiceFrontResponse();
        response.setInvoiceList(invoiceList);
        if (CollUtil.isEmpty(invoiceList)) {
            response.setNum(1);
            response.setDeliveryNum(0);
            return response;
        }
        response.setNum(order.getStatus().equals(OrderConstants.ORDER_STATUS_PART_SHIPPING) ? invoiceList.size() + 1 : invoiceList.size());
        response.setDeliveryNum(invoiceList.size());
        return response;
    }

    /**
     * 获取个人中心订单数量
     */
    @Override
    public OrderCenterNumResponse userCenterNum() {
        Integer userId = userService.getUserIdException();
        OrderCenterNumResponse response = new OrderCenterNumResponse();
        response.setAwaitPayCount(orderService.getCountByStatusAndUid(OrderConstants.ORDER_STATUS_WAIT_PAY, userId));
        response.setAwaitShippedCount(orderService.getCountByStatusAndUid(OrderConstants.ORDER_STATUS_WAIT_SHIPPING, userId));
        response.setReceiptCount(orderService.getCountByStatusAndUid(OrderConstants.ORDER_STATUS_WAIT_RECEIPT, userId));
        response.setVerificationCount(orderService.getCountByStatusAndUid(OrderConstants.ORDER_STATUS_AWAIT_VERIFICATION, userId));
        response.setAwaitReplyCount(orderDetailService.getAwaitReplyCount(userId));
        response.setRefundCount(refundOrderService.getRefundingCount(userId));
        return response;
    }

    /**
     * 获取订单状态图
     */
    @Override
    public List<HashMap<String, Object>> getOrderStatusImage() {
        List<HashMap<String, Object>> mapList = systemGroupDataService.getListMapByGid(GroupDataConstants.GROUP_DATA_ID_ORDER_STATUS_IMAGE);
        return mapList;
    }

    private List<MyRecord> validateProductStock(PreOrderInfoVo orderInfoVo) {
        List<MyRecord> recordList = CollUtil.newArrayList();
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {// 秒杀订单
            recordList.add(seckillService.validateCreateOrderProductStock(orderInfoVo));
            return recordList;
        }
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_VIDEO)) {// 视频号订单
            // 查询商品信息 视频号都是单品下单
            List<PreOrderInfoDetailVo> detailVos = orderInfoVo.getMerchantOrderVoList().get(0).getOrderInfoList();
            PayComponentProduct product = payComponentProductService.getById(detailVos.get(0).getProductId());
            if (ObjectUtil.isNull(product)) {
                throw new CrmebException("商品信息不存在，请刷新后重新选择");
            }
            if (product.getIsDel()) {
                throw new CrmebException("商品已删除，请刷新后重新选择");
            }
            if (!product.getStatus().equals(5)) {
                throw new CrmebException("商品已下架，请刷新后重新选择");
            }
            if (product.getStock().equals(0) || product.getStock() < detailVos.get(0).getPayNum()) {
                throw new CrmebException("商品库存不足，请刷新后重新选择");
            }
            // 查询商品规格属性值信息
            ProductAttrValue attrValue = productAttrValueService.getByIdAndProductIdAndType(detailVos.get(0).getAttrValueId(), detailVos.get(0).getProductId(), ProductConstants.PRODUCT_TYPE_COMPONENT);
            if (ObjectUtil.isNull(attrValue)) {
                throw new CrmebException("商品规格信息不存在，请刷新后重新选择");
            }
            if (attrValue.getStock().equals(0) || attrValue.getStock() < detailVos.get(0).getPayNum()) {
                throw new CrmebException("商品规格库存不足，请刷新后重新选择");
            }
            // 查询视频商品规格属性值
            PayComponentProductSku productSku = payComponentProductSkuService.getByProIdAndAttrValueId(product.getId(), attrValue.getId());
            if (ObjectUtil.isNull(productSku)) {
                throw new CrmebException("商品sku信息不存在，请刷新后重新选择");
            }
            if (productSku.getStockNum().equals(0) || productSku.getStockNum() < detailVos.get(0).getPayNum()) {
                throw new CrmebException("商品sku库存不足，请刷新后重新选择");
            }

            MyRecord record = new MyRecord();
            record.set("productId", product.getId());
            record.set("attrValueId", detailVos.get(0).getAttrValueId());
            record.set("attrValueVersion", attrValue.getVersion());
            record.set("num", detailVos.get(0).getPayNum());
            record.set("skuId", productSku.getId());
            record.set("skuVersion", productSku.getVersion());
            recordList.add(record);
            return recordList;
        }
        // 普通商品
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        merchantOrderVoList.forEach(merchantOrderVo -> {
            Merchant merchant = merchantService.getByIdException(merchantOrderVo.getMerId());
            if (!merchant.getIsSwitch()) {
                throw new CrmebException("商户已关闭，请重新下单");
            }
            merchantOrderVo.getOrderInfoList().forEach(info -> {
                // 查询商品信息
                Product product = productService.getById(info.getProductId());
                if (ObjectUtil.isNull(product) || product.getIsDel()) {
                    throw new CrmebException("购买的商品信息不存在");
                }
                if (!product.getIsShow()) {
                    throw new CrmebException("购买的商品已下架");
                }
                if (product.getStock().equals(0) || info.getPayNum() > product.getStock()) {
                    throw new CrmebException("购买的商品库存不足");
                }
                // 查询商品规格属性值信息
                ProductAttrValue attrValue = productAttrValueService.getByIdAndProductIdAndType(info.getAttrValueId(), info.getProductId(), ProductConstants.PRODUCT_TYPE_NORMAL);
                if (ObjectUtil.isNull(attrValue)) {
                    throw new CrmebException("购买的商品规格信息不存在");
                }
                if (attrValue.getStock() < info.getPayNum()) {
                    throw new CrmebException("购买的商品库存不足");
                }
                MyRecord record = new MyRecord();
                record.set("productId", info.getProductId());
                record.set("num", info.getPayNum());
                record.set("attrValueId", info.getAttrValueId());
                record.set("attrValueVersion", attrValue.getVersion());
                recordList.add(record);
            });
        });
        return recordList;
    }

    /**
     * 计算订单运费
     */
    @Override
    public void getFreightFee(PreOrderInfoVo orderInfoVo, UserAddress userAddress) {
        BigDecimal freightFee = BigDecimal.ZERO;

        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            BigDecimal storePostage = BigDecimal.ZERO;
            if (merchantOrderVo.getShippingType().equals(OrderConstants.ORDER_SHIPPING_TYPE_PICK_UP)) {
                merchantOrderVo.setFreightFee(storePostage);
                continue;
            }
            if (ObjectUtil.isNull(userAddress) || userAddress.getCityId() <= 0) {
                merchantOrderVo.setFreightFee(storePostage);
                continue;
            }
            // 运费根据商品计算
            Map<Integer, MyRecord> proMap = new HashMap<>();
            merchantOrderVo.getOrderInfoList().forEach(e -> {
                Integer proId = e.getProductId();
                if (proMap.containsKey(proId)) {
                    MyRecord record = proMap.get(proId);
                    record.set("totalPrice", record.getBigDecimal("totalPrice").add(e.getPrice().multiply(BigDecimal.valueOf(e.getPayNum()))));
                    record.set("totalNum", record.getInt("totalNum") + e.getPayNum());
                    BigDecimal weight = e.getWeight().multiply(BigDecimal.valueOf(e.getPayNum()));
                    record.set("weight", record.getBigDecimal("weight").add(weight));
                    BigDecimal volume = e.getVolume().multiply(BigDecimal.valueOf(e.getPayNum()));
                    record.set("volume", record.getBigDecimal("volume").add(volume));
                } else {
                    MyRecord record = new MyRecord();
                    record.set("totalPrice", e.getPrice().multiply(BigDecimal.valueOf(e.getPayNum())));
                    record.set("totalNum", e.getPayNum());
                    record.set("tempId", e.getTempId());
                    record.set("proId", proId);
                    BigDecimal weight = e.getWeight().multiply(BigDecimal.valueOf(e.getPayNum()));
                    record.set("weight", weight);
                    BigDecimal volume = e.getVolume().multiply(BigDecimal.valueOf(e.getPayNum()));
                    record.set("volume", volume);
                    proMap.put(proId, record);
                }
            });

            // 指定包邮（单品运费模板）> 指定区域配送（单品运费模板）
            int districtId = userAddress.getDistrictId();
            for (Map.Entry<Integer, MyRecord> m : proMap.entrySet()) {
                MyRecord record = m.getValue();
                Integer tempId = record.getInt("tempId");
                ShippingTemplates shippingTemplate = shippingTemplatesService.getById(tempId);
                if (ObjectUtil.isNull(shippingTemplate) || shippingTemplate.getAppoint().equals(ShippingTemplatesConstants.APPOINT_TYPE_ALL)) {
                    continue;
                }
                if (shippingTemplate.getAppoint().equals(ShippingTemplatesConstants.APPOINT_TYPE_DEFINED)) {
                    ShippingTemplatesFree shippingTemplatesFree = shippingTemplatesFreeService.getByTempIdAndCityId(tempId, districtId);
                    if (ObjectUtil.isNotNull(shippingTemplatesFree)) {
                        BigDecimal totalPrice = record.getBigDecimal("totalPrice");
                        if (totalPrice.compareTo(shippingTemplatesFree.getPrice()) >= 0) {
                            continue;
                        }
                        if (shippingTemplate.getType().equals(ShippingTemplatesConstants.CHARGE_MODE_TYPE_NUMBER)) {
                            if (BigDecimal.valueOf(record.getInt("totalNum")).compareTo(shippingTemplatesFree.getNumber()) >= 0) {
                                continue;
                            }
                        }
                        BigDecimal surplus = shippingTemplate.getType().equals(ShippingTemplatesConstants.CHARGE_MODE_TYPE_WEIGHT) ? record.getBigDecimal("weight") : record.getBigDecimal("volume");
                        if (surplus.compareTo(shippingTemplatesFree.getNumber()) >= 0) {
                            continue;
                        }
                    }
                }
                ShippingTemplatesRegion shippingTemplatesRegion = shippingTemplatesRegionService.getByTempIdAndCityId(tempId, districtId);
                if (ObjectUtil.isNull(shippingTemplatesRegion)) {
                    shippingTemplatesRegion = shippingTemplatesRegionService.getByTempIdAndCityId(tempId, 0);
                }
                if (shippingTemplate.getAppoint().equals(ShippingTemplatesConstants.APPOINT_TYPE_PART)) {
                    if (ObjectUtil.isNull(shippingTemplatesRegion)) {
                        continue;
                    }
                }
                BigDecimal postageFee = BigDecimal.ZERO;
                // 判断计费方式：件数、重量、体积
                switch (shippingTemplate.getType()) {
                    case ShippingTemplatesConstants.CHARGE_MODE_TYPE_NUMBER: // 件数
                        // 判断件数是否超过首件
                        Integer num = record.getInt("totalNum");
                        if (num <= shippingTemplatesRegion.getFirst().intValue()) {
                            storePostage = storePostage.add(shippingTemplatesRegion.getFirstPrice());
                            postageFee = shippingTemplatesRegion.getFirstPrice();
                        } else {// 超过首件的需要计算续件
                            int renewalNum = num - shippingTemplatesRegion.getFirst().intValue();
                            // 剩余件数/续件 = 需要计算的续件费用的次数
                            BigDecimal divide = BigDecimal.valueOf(renewalNum).divide(shippingTemplatesRegion.getRenewal(), 0, BigDecimal.ROUND_UP);
                            BigDecimal renewalPrice = shippingTemplatesRegion.getRenewalPrice().multiply(divide);
                            storePostage = storePostage.add(shippingTemplatesRegion.getFirstPrice()).add(renewalPrice);
                            postageFee = shippingTemplatesRegion.getFirstPrice().add(renewalPrice);
                        }
                        List<PreOrderInfoDetailVo> detailVoList = merchantOrderVo.getOrderInfoList().stream().filter(e -> e.getProductId().equals(record.getInt("proId"))).collect(Collectors.toList());
                        if (detailVoList.size() == 1) {
                            detailVoList.get(0).setFreightFee(postageFee);
                        } else {
                            for (int i = 0; i < detailVoList.size(); i++) {
                                PreOrderInfoDetailVo detail = detailVoList.get(i);
                                if (detailVoList.size() == (i + 1)) {
                                    detail.setFreightFee(postageFee);
                                    break;
                                }
                                BigDecimal ratio = new BigDecimal(detail.getPayNum().toString()).divide(new BigDecimal(num.toString()), 10, BigDecimal.ROUND_HALF_UP);
                                BigDecimal multiply = postageFee.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                                detail.setFreightFee(multiply);
                                postageFee = postageFee.subtract(multiply);
                            }
                        }
                        break;
                    case ShippingTemplatesConstants.CHARGE_MODE_TYPE_WEIGHT: // 重量
                    case ShippingTemplatesConstants.CHARGE_MODE_TYPE_VOLIME: // 体积
                        BigDecimal surplus = shippingTemplate.getType().equals(ShippingTemplatesConstants.CHARGE_MODE_TYPE_WEIGHT) ? record.getBigDecimal("weight") : record.getBigDecimal("volume");
                        if (surplus.compareTo(shippingTemplatesRegion.getFirst()) <= 0) {
                            storePostage = storePostage.add(shippingTemplatesRegion.getFirstPrice());
                            postageFee = shippingTemplatesRegion.getFirstPrice();
                        } else {// 超过首件的需要计算续件
                            BigDecimal renewalNum = surplus.subtract(shippingTemplatesRegion.getFirst());
                            // 剩余件数/续件 = 需要计算的续件费用的次数
                            BigDecimal divide = renewalNum.divide(shippingTemplatesRegion.getRenewal(), 0, BigDecimal.ROUND_UP);
                            BigDecimal renewalPrice = shippingTemplatesRegion.getRenewalPrice().multiply(divide);
                            storePostage = storePostage.add(shippingTemplatesRegion.getFirstPrice()).add(renewalPrice);
                            postageFee = shippingTemplatesRegion.getFirstPrice().add(renewalPrice);
                        }
                        List<PreOrderInfoDetailVo> infoDetailVoList = merchantOrderVo.getOrderInfoList().stream().filter(e -> e.getProductId().equals(record.getInt("proId"))).collect(Collectors.toList());
                        if (infoDetailVoList.size() == 1) {
                            infoDetailVoList.get(0).setFreightFee(postageFee);
                        } else {
                            for (int i = 0; i < infoDetailVoList.size(); i++) {
                                PreOrderInfoDetailVo detail = infoDetailVoList.get(i);
                                if (infoDetailVoList.size() == (i + 1)) {
                                    detail.setFreightFee(postageFee);
                                    break;
                                }
                                BigDecimal wv = shippingTemplate.getType().equals(ShippingTemplatesConstants.CHARGE_MODE_TYPE_WEIGHT) ? detail.getWeight() : detail.getVolume();
                                BigDecimal ratio = wv.multiply(new BigDecimal(detail.getPayNum().toString())).divide(surplus, 10, BigDecimal.ROUND_HALF_UP);
                                BigDecimal multiply = postageFee.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                                detail.setFreightFee(multiply);
                                postageFee = postageFee.subtract(multiply);
                            }
                        }
                        break;
                }
            }
            merchantOrderVo.setFreightFee(storePostage);
            freightFee = freightFee.add(storePostage);
        }
        orderInfoVo.setFreightFee(freightFee);
    }

    /**
     * 校验预下单商品信息
     *
     * @param request 预下单请求参数
     * @return OrderInfoVo
     */
    private PreOrderInfoVo validatePreOrderRequest(PreOrderRequest request, User user) {
        PreOrderInfoVo preOrderInfoVo = new PreOrderInfoVo();
        List<PreMerchantOrderVo> merchantOrderVoList = CollUtil.newArrayList();
        if (request.getPreOrderType().equals("shoppingCart")) {// 购物车购买
            logger.info("预下单检查调用:购物车购买");
            merchantOrderVoList = validatePreOrderShopping(request, user);
            List<Integer> cartIdList = request.getOrderDetails().stream().map(PreOrderDetailRequest::getShoppingCartId).distinct().collect(Collectors.toList());
            preOrderInfoVo.setCartIdList(cartIdList);
        }
        if (request.getPreOrderType().equals("buyNow")) {// 立即购买
            logger.info("预下单检查调用:立即购买");
            // 立即购买只会有一条详情
            PreOrderDetailRequest detailRequest = request.getOrderDetails().get(0);
            merchantOrderVoList.add(validatePreOrderNormal(detailRequest));
            preOrderInfoVo.setType(OrderType.PURCHASE.code());
        }
        if (request.getPreOrderType().equals("video")) {
            logger.info("预下单检查调用:视频号下单");
            // 视频号暂时只能购买一个商品
            PreOrderDetailRequest detailRequest = request.getOrderDetails().get(0);
            merchantOrderVoList.add(validatePreOrderVideo(detailRequest));
            preOrderInfoVo.setType(OrderConstants.ORDER_TYPE_VIDEO);
        }
        if (request.getPreOrderType().equals("seckill")) {
            logger.info("预下单检查调用:秒杀下单");
            // 秒杀只支持单商品支付
            PreOrderDetailRequest detailRequest = request.getOrderDetails().get(0);
            merchantOrderVoList.add(seckillService.validatePreOrderSeckill(detailRequest));
            preOrderInfoVo.setType(OrderConstants.ORDER_TYPE_SECKILL);
        }
        if (StringUtils.equalsIgnoreCase(request.getPreOrderType(), "rentNow")) {
            // 正常共享下单只会有一条记录
            Assert.isTrue(CollectionUtils.size(request.getOrderDetails()) == 1, "共享商品只能购买一个商品");
            PreOrderDetailRequest detailRequest = request.getOrderDetails().get(0);
            merchantOrderVoList.add(validatePreOrderRent(detailRequest));
            preOrderInfoVo.setType(OrderType.RENT.code());
        }
        preOrderInfoVo.setMerchantOrderVoList(merchantOrderVoList);
        logger.info("预下单检查调用:结果preOrderInfoVo:{}", JSON.toJSONString(preOrderInfoVo));
        return preOrderInfoVo;
    }

    /**
     * 普通商品下单校验
     *
     * @param detailRequest 商品参数
     */
    private PreMerchantOrderVo validatePreOrderNormal(PreOrderDetailRequest detailRequest) {
        // 普通商品
        if (ObjectUtil.isNull(detailRequest.getProductId())) {
            throw new CrmebException("商品编号不能为空");
        }
        if (ObjectUtil.isNull(detailRequest.getAttrValueId())) {
            throw new CrmebException("商品规格属性值不能为空");
        }
        if (ObjectUtil.isNull(detailRequest.getProductNum()) || detailRequest.getProductNum() <= 0) {
            throw new CrmebException("购买数量必须大于0");
        }
        // 查询商品信息
        Product product = productService.getById(detailRequest.getProductId());
        if (ObjectUtil.isNull(product) || product.getIsDel()) {
            throw new CrmebException("商品信息不存在，请刷新后重新选择");
        }
        if (!product.getIsShow()) {
            throw new CrmebException("商品已下架，请刷新后重新选择");
        }
        if (product.getStock() < detailRequest.getProductNum()) {
            throw new CrmebException("商品库存不足，请刷新后重新选择");
        }
        // 查询商品规格属性值信息
        ProductAttrValue attrValue = productAttrValueService.getByIdAndProductIdAndType(detailRequest.getAttrValueId(), detailRequest.getProductId(), ProductConstants.PRODUCT_TYPE_NORMAL);
        if (ObjectUtil.isNull(attrValue)) {
            throw new CrmebException("商品规格信息不存在，请刷新后重新选择");
        }
        if (attrValue.getStock() < detailRequest.getProductNum()) {
            throw new CrmebException("商品规格库存不足，请刷新后重新选择");
        }
        Merchant merchant = merchantService.getByIdException(product.getMerId());
        if (!merchant.getIsSwitch()) {
            throw new CrmebException("商户已关闭，请重新选择商品");
        }

        PreMerchantOrderVo merchantOrderVo = new PreMerchantOrderVo();
        merchantOrderVo.setMerId(merchant.getId());
        merchantOrderVo.setMerName(merchant.getName());
        merchantOrderVo.setFreightFee(BigDecimal.ZERO);
        merchantOrderVo.setCouponFee(BigDecimal.ZERO);
        merchantOrderVo.setUserCouponId(0);
        merchantOrderVo.setTakeTheirSwitch(merchant.getIsTakeTheir());
        PreOrderInfoDetailVo detailVo = new PreOrderInfoDetailVo();
        detailVo.setProductId(product.getId());
        detailVo.setProductName(product.getName());
        detailVo.setAttrValueId(attrValue.getId());
        detailVo.setSku(attrValue.getSku());
        detailVo.setPrice(attrValue.getPrice());
        detailVo.setRentPrice(attrValue.getRentPrice());
        detailVo.setPayPrice(attrValue.getPrice());
        detailVo.setPayNum(detailRequest.getProductNum());
        detailVo.setImage(StrUtil.isNotBlank(attrValue.getImage()) ? attrValue.getImage() : product.getImage());
        detailVo.setVolume(attrValue.getVolume());
        detailVo.setWeight(attrValue.getWeight());
        detailVo.setTempId(product.getTempId());
        detailVo.setSubBrokerageType(product.getIsSub() ? 1 : 2);
        detailVo.setBrokerage(attrValue.getBrokerage());
        detailVo.setBrokerageTwo(attrValue.getBrokerageTwo());
        if (detailVo.getSubBrokerageType() == 2) {
            String firstRatio = systemConfigService.getValueByKey(SysConfigConstants.RETAIL_STORE_BROKERAGE_FIRST_RATIO);
            String secondRatio = systemConfigService.getValueByKey(SysConfigConstants.RETAIL_STORE_BROKERAGE_SECOND_RATIO);
            detailVo.setBrokerage(StrUtil.isNotBlank(firstRatio) ? Integer.parseInt(firstRatio) : 0);
            detailVo.setBrokerageTwo(StrUtil.isNotBlank(secondRatio) ? Integer.parseInt(secondRatio) : 0);
        }
        detailVo.setProductType(ProductConstants.PRODUCT_TYPE_NORMAL);
        detailVo.setRentDays(detailRequest.getRentDays());
        List<PreOrderInfoDetailVo> infoList = CollUtil.newArrayList();
        infoList.add(detailVo);
        merchantOrderVo.setOrderInfoList(infoList);
        return merchantOrderVo;
    }


    /**
     * 共享商品下单校验
     *
     * @param detailRequest 商品参数
     */
    private PreMerchantOrderVo validatePreOrderRent(PreOrderDetailRequest detailRequest) {
        // 普通商品
        if (ObjectUtil.isNull(detailRequest.getProductId())) {
            throw new CrmebException("商品编号不能为空");
        }
        if (ObjectUtil.isNull(detailRequest.getAttrValueId())) {
            throw new CrmebException("商品规格属性值不能为空");
        }
        if (ObjectUtil.isNull(detailRequest.getProductNum()) || detailRequest.getProductNum() <= 0) {
            throw new CrmebException("购买数量必须大于0");
        }
        // 查询商品信息
        Product product = productService.getById(detailRequest.getProductId());
        if (ObjectUtil.isNull(product) || product.getIsDel()) {
            throw new CrmebException("商品信息不存在，请刷新后重新选择");
        }
        if (!product.getIsShow()) {
            throw new CrmebException("商品已下架，请刷新后重新选择");
        }
        if (product.getStock() < detailRequest.getProductNum()) {
            throw new CrmebException("商品库存不足，请刷新后重新选择");
        }
        // 查询商品规格属性值信息
        ProductAttrValue attrValue = productAttrValueService.getByIdAndProductIdAndType(detailRequest.getAttrValueId(), detailRequest.getProductId(), ProductConstants.PRODUCT_TYPE_NORMAL);
        if (ObjectUtil.isNull(attrValue)) {
            throw new CrmebException("商品规格信息不存在，请刷新后重新选择");
        }
        if (attrValue.getStock() < detailRequest.getProductNum()) {
            throw new CrmebException(OrderPayResultCode.ORDER_USER_CANCEL);
        }
        Merchant merchant = merchantService.getByIdException(product.getMerId());
        if (!merchant.getIsSwitch()) {
            throw new CrmebException("商户已关闭，请重新选择商品");
        }

        // 共享商品下单时：
        // 1、需要检测当前用户的信用额度是否足够（即大于等于商品销售价）。
        BigDecimal price = product.getPrice();
        if (price.compareTo(BigDecimal.ZERO) > 0) {
//            throw new CrmebException(OrderPayResultCode.ORDER_RENT_QUOTA_LESS);
        }

        PreMerchantOrderVo merchantOrderVo = new PreMerchantOrderVo();
        merchantOrderVo.setMerId(merchant.getId());
        merchantOrderVo.setMerName(merchant.getName());
        merchantOrderVo.setFreightFee(BigDecimal.ZERO);
        merchantOrderVo.setCouponFee(BigDecimal.ZERO);
        merchantOrderVo.setUserCouponId(0);
        merchantOrderVo.setTakeTheirSwitch(merchant.getIsTakeTheir());
        PreOrderInfoDetailVo detailVo = new PreOrderInfoDetailVo();
        detailVo.setProductId(product.getId());
        detailVo.setProductName(product.getName());
        detailVo.setAttrValueId(attrValue.getId());
        detailVo.setSku(attrValue.getSku());
        //共享下单，单价中存放共享单价
//        BigDecimal payPrice = attrValue.getRentPrice().multiply(BigDecimal.valueOf(detailRequest.getRentDays().longValue()));
        detailVo.setPrice(attrValue.getPrice());
        detailVo.setRentPrice(attrValue.getRentPrice());
        //共享下单，支付价格需要计算
        detailVo.setPayPrice(attrValue.getRentPrice().multiply(BigDecimal.valueOf(detailRequest.getRentDays().longValue())));
        detailVo.setPayNum(detailRequest.getProductNum());
        detailVo.setImage(StrUtil.isNotBlank(attrValue.getImage()) ? attrValue.getImage() : product.getImage());
        detailVo.setVolume(attrValue.getVolume());
        detailVo.setWeight(attrValue.getWeight());
        detailVo.setTempId(product.getTempId());
        detailVo.setSubBrokerageType(product.getIsSub() ? 1 : 2);
        detailVo.setBrokerage(attrValue.getBrokerage());
        detailVo.setBrokerageTwo(attrValue.getBrokerageTwo());
        if (detailVo.getSubBrokerageType() == 2) {
            String firstRatio = systemConfigService.getValueByKey(SysConfigConstants.RETAIL_STORE_BROKERAGE_FIRST_RATIO);
            String secondRatio = systemConfigService.getValueByKey(SysConfigConstants.RETAIL_STORE_BROKERAGE_SECOND_RATIO);
            detailVo.setBrokerage(StrUtil.isNotBlank(firstRatio) ? Integer.parseInt(firstRatio) : 0);
            detailVo.setBrokerageTwo(StrUtil.isNotBlank(secondRatio) ? Integer.parseInt(secondRatio) : 0);
        }
        detailVo.setProductType(ProductConstants.PRODUCT_TYPE_NORMAL);
        detailVo.setRentDays(detailRequest.getRentDays());
        List<PreOrderInfoDetailVo> infoList = CollUtil.newArrayList();
        infoList.add(detailVo);
        merchantOrderVo.setOrderInfoList(infoList);
        return merchantOrderVo;
    }

    /**
     * 购物车预下单校验
     *
     * @param request 请求参数
     * @param user    用户
     * @return List<PreMerchantOrderVo>
     */
    private List<PreMerchantOrderVo> validatePreOrderShopping(PreOrderRequest request, User user) {
        List<PreMerchantOrderVo> merchantOrderVoList = CollUtil.newArrayList();
        request.getOrderDetails().forEach(e -> {
            if (ObjectUtil.isNull(e.getShoppingCartId())) {
                throw new CrmebException("购物车编号不能为空");
            }
            Cart cart = cartService.getByIdAndUid(e.getShoppingCartId(), user.getId());
            if (ObjectUtil.isNull(cart)) {
                throw new CrmebException("未找到对应的购物车信息");
            }
            e.setProductId(cart.getProductId());
            e.setAttrValueId(cart.getProductAttrUnique());
            e.setProductNum(cart.getCartNum());
            PreMerchantOrderVo merchantOrderVo = validatePreOrderNormal(e);
            if (merchantOrderVoList.stream().anyMatch(m -> m.getMerId().equals(merchantOrderVo.getMerId()))) {
                for (PreMerchantOrderVo orderVo : merchantOrderVoList) {
                    if (orderVo.getMerId().equals(merchantOrderVo.getMerId())) {
                        orderVo.getOrderInfoList().addAll(merchantOrderVo.getOrderInfoList());
                        break;
                    }
                }
            } else {
                merchantOrderVoList.add(merchantOrderVo);
            }
        });
        return merchantOrderVoList;
    }

    private ComputedOrderPriceResponse computedPrice_V1_3(OrderComputedPriceRequest request, PreOrderInfoVo orderInfoVo, User user) {
        String integralDeductionSwitch = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_SWITCH);
        if (request.getIsUseIntegral()) {
            if (integralDeductionSwitch.equals("false")) {
                throw new CrmebException("积分抵扣未开启，请重新下单");
            }
        }

        // 计算各种价格
        ComputedOrderPriceResponse priceResponse = new ComputedOrderPriceResponse();
        List<OrderMerchantRequest> orderMerchantRequestList = request.getOrderMerchantRequestList();
        // 计算运费
        UserAddress userAddress = userAddressService.getById(request.getAddressId());
        orderInfoVo.getMerchantOrderVoList().forEach(e -> {
            orderMerchantRequestList.forEach(o -> {
                if (o.getMerId().equals(e.getMerId())) {
                    e.setShippingType(o.getShippingType());
                    e.setUserCouponId(o.getUserCouponId());
                }
            });
        });
        getFreightFee(orderInfoVo, userAddress);
        priceResponse.setFreightFee(orderInfoVo.getFreightFee());
        // 优惠券计算
        orderInfoVo.setPlatUserCouponId(request.getPlatUserCouponId());
        getCouponFee(orderInfoVo, orderMerchantRequestList, user.getId());
        priceResponse.setCouponFee(orderInfoVo.getCouponFee());
        List<ComputedMerchantOrderResponse> merOrderResponseList = orderInfoVo.getMerchantOrderVoList().stream().map(vo -> {
            ComputedMerchantOrderResponse merOrderResponse = new ComputedMerchantOrderResponse();
            merOrderResponse.setMerId(vo.getMerId());
            merOrderResponse.setUserCouponId(vo.getUserCouponId());
            merOrderResponse.setCouponFee(vo.getCouponFee());
            merOrderResponse.setFreightFee(vo.getFreightFee());
            return merOrderResponse;
        }).collect(Collectors.toList());
        priceResponse.setMerOrderResponseList(merOrderResponseList);
        // 积分部分
        BigDecimal payPrice = orderInfoVo.getProTotalFee().subtract(priceResponse.getCouponFee());
        priceResponse.setIsUseIntegral(request.getIsUseIntegral());
        priceResponse.setProTotalFee(orderInfoVo.getProTotalFee());
        if (!request.getIsUseIntegral() || user.getIntegral() <= 0) {// 不使用积分
            priceResponse.setDeductionPrice(BigDecimal.ZERO);
//            priceResponse.setSurplusIntegral(user.getIntegral());
            priceResponse.setAvailableBalance(user.getNowMoney());
            priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
            priceResponse.setUsedIntegral(0);
            priceResponse.setIntegralDeductionSwitch(false);
            priceResponse.setIsUseIntegral(false);
            String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
            if ("ture".equals(integralDeductionSwitch) && payPrice.compareTo(new BigDecimal(integralDeductionStartMoney)) >= 0) {
                priceResponse.setIntegralDeductionSwitch(true);
            }
            return priceResponse;
        }
        // 使用积分
        if (request.getIsUseIntegral() && user.getIntegral() > 0) {
            String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
            if (Integer.parseInt(integralDeductionStartMoney) <= 0 || payPrice.compareTo(new BigDecimal(integralDeductionStartMoney)) < 0) {
                priceResponse.setDeductionPrice(BigDecimal.ZERO);
                priceResponse.setAvailableBalance(user.getNowMoney());
//                priceResponse.setSurplusIntegral(user.getIntegral());
                priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
                priceResponse.setUsedIntegral(0);
                priceResponse.setIntegralDeductionSwitch(false);
                priceResponse.setIsUseIntegral(false);
                return priceResponse;
            }

            // 查询积分使用比例
            String integralRatio = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_RATIO);
            // 可抵扣金额
            BigDecimal canDeductionPrice = payPrice.multiply(new BigDecimal(integralRatio)).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
            // 积分转换金额
            String integralDeductionMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_MONEY);
            BigDecimal deductionPrice = new BigDecimal(user.getIntegral()).multiply(new BigDecimal(integralDeductionMoney));
            // 积分兑换金额小于实际支付可抵扣金额
            if (deductionPrice.compareTo(canDeductionPrice) <= 0) {
//                payPrice = payPrice.subtract(deductionPrice);
//                priceResponse.setSurplusIntegral(0);
                priceResponse.setAvailableBalance(BigDecimal.ZERO);
                priceResponse.setUsedIntegral(user.getIntegral());
            } else {
                deductionPrice = canDeductionPrice;
                if (canDeductionPrice.compareTo(BigDecimal.ZERO) > 0) {
                    int usedIntegral = canDeductionPrice.divide(new BigDecimal(integralDeductionMoney), 0, BigDecimal.ROUND_UP).intValue();
//                    priceResponse.setSurplusIntegral(user.getIntegral() - usedIntegral);
                    priceResponse.setAvailableBalance(user.getNowMoney().subtract(canDeductionPrice));
                    priceResponse.setUsedIntegral(usedIntegral);
                }
            }
            payPrice = payPrice.subtract(deductionPrice);
            priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
            priceResponse.setDeductionPrice(deductionPrice);
            priceResponse.setIsUseIntegral(true);
            priceResponse.setIntegralDeductionSwitch(true);
        }
        return priceResponse;
    }

    private ComputedOrderPriceResponse computedPrice(OrderComputedPriceRequest request, PreOrderInfoVo orderInfoVo, User user) {
        String integralDeductionSwitch = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_SWITCH);
        if (request.getIsUseIntegral()) {
            if (integralDeductionSwitch.equals("false")) {
                throw new CrmebException("积分抵扣未开启，请重新下单");
            }
        }

        // 计算各种价格
        ComputedOrderPriceResponse priceResponse = new ComputedOrderPriceResponse();
        List<OrderMerchantRequest> orderMerchantRequestList = request.getOrderMerchantRequestList();
        // 计算运费
        UserAddress userAddress = userAddressService.getById(request.getAddressId());
        orderInfoVo.getMerchantOrderVoList().forEach(e -> {
            orderMerchantRequestList.forEach(o -> {
                if (o.getMerId().equals(e.getMerId())) {
                    e.setShippingType(o.getShippingType());
                    e.setUserCouponId(o.getUserCouponId());
                    e.setCouponFee(BigDecimal.ZERO);
                    e.setMerCouponFee(BigDecimal.ZERO);
                }
            });
        });
        // TODO 未来可能依据地址计算运费
        // getFreightFee(orderInfoVo, userAddress);
        priceResponse.setFreightFee(BigDecimal.ZERO);
        // 优惠券计算
        orderInfoVo.setCouponFee(BigDecimal.ZERO);
        orderInfoVo.setPlatUserCouponId(request.getPlatUserCouponId());
        orderInfoVo.setPlatCouponFee(BigDecimal.ZERO);
        orderInfoVo.setMerCouponFee(BigDecimal.ZERO);
        priceResponse.setCouponFee(BigDecimal.ZERO);
        if (!orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_SECKILL) && !orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_VIDEO)) {
            getCouponFee_V1_3(orderInfoVo, user.getId());
            priceResponse.setCouponFee(orderInfoVo.getCouponFee());
            priceResponse.setPlatCouponFee(orderInfoVo.getPlatCouponFee());
            priceResponse.setMerCouponFee(orderInfoVo.getMerCouponFee());
            priceResponse.setMerOrderResponseList(computedPriceGetMerOrderList(orderInfoVo));
            priceResponse.setPlatCouponUserList(computedPriceGetPlatOrderList(orderInfoVo));
        }


        // 积分部分
        BigDecimal payPrice = orderInfoVo.getProTotalFee().subtract(priceResponse.getCouponFee());
        priceResponse.setIsUseIntegral(request.getIsUseIntegral());
        priceResponse.setProTotalFee(orderInfoVo.getProTotalFee());
        UserFundAccount userFundAccount = fundManager.fundAccount(user.getId(), FundAccountType.CRAB_ROE.name());
        if (!request.getIsUseIntegral() || userFundAccount.getAvailableAmount().compareTo(BigDecimal.ZERO) <= 0) {// 不使用积分
            priceResponse.setDeductionPrice(BigDecimal.ZERO);
//            priceResponse.setSurplusIntegral(user.getIntegral());
            priceResponse.setAvailableBalance(user.getNowMoney());
            priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
            priceResponse.setUsedIntegral(0);
            priceResponse.setIntegralDeductionSwitch(false);
            priceResponse.setIsUseIntegral(false);
            String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
            if ("ture".equals(integralDeductionSwitch) && payPrice.compareTo(new BigDecimal(integralDeductionStartMoney)) >= 0) {
                priceResponse.setIntegralDeductionSwitch(true);
            }
            return priceResponse;
        }
        // 使用积分
        if (request.getIsUseIntegral() && userFundAccount.getAvailableAmount().compareTo(BigDecimal.ZERO) > 0) {

            UseAvailableResultDTO useAvailableResultDTO = fundManager.getAvailableAmount(user, payPrice, FundAccountType.CRAB_ROE.name());

            if (payPrice.subtract(useAvailableResultDTO.getRealAmount()).compareTo(BigDecimal.ZERO) == 0) {
                priceResponse.setPayFee(payPrice.subtract(useAvailableResultDTO.getRealAmount()).add(new BigDecimal("0.01")));
                // 雅识抵扣金额
                priceResponse.setDeductionPrice(useAvailableResultDTO.getRealAmount().subtract(new BigDecimal("0.01")));
            } else {
                priceResponse.setPayFee(payPrice.subtract(useAvailableResultDTO.getRealAmount()));
                // 雅识抵扣金额
                priceResponse.setDeductionPrice(useAvailableResultDTO.getRealAmount());
            }
            priceResponse.setIsUseIntegral(true);
            priceResponse.setIntegralDeductionSwitch(true);

            return priceResponse;
//            // 目的就是不走后边的逻辑
//            if (true) {
//            }
//
//            String integralDeductionStartMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_START_MONEY);
//            if (Integer.parseInt(integralDeductionStartMoney) <= 0 || payPrice.compareTo(new BigDecimal(integralDeductionStartMoney)) < 0) {
//                priceResponse.setDeductionPrice(BigDecimal.ZERO);
////                priceResponse.setSurplusIntegral(user.getIntegral());
//                priceResponse.setAvailableBalance(user.getNowMoney());
//                priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
//                priceResponse.setUsedIntegral(0);
//                priceResponse.setIntegralDeductionSwitch(false);
//                priceResponse.setIsUseIntegral(false);
//                return priceResponse;
//            }
//
//            // 查询积分使用比例
//            String integralRatio = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_RATIO);
//            // 可抵扣金额
//            BigDecimal canDeductionPrice = payPrice.multiply(new BigDecimal(integralRatio)).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN);
//            // 积分转换金额
//            String integralDeductionMoney = systemConfigService.getValueByKey(SysConfigConstants.CONFIG_KEY_INTEGRAL_DEDUCTION_MONEY);
//            BigDecimal deductionPrice = new BigDecimal(user.getIntegral()).multiply(new BigDecimal(integralDeductionMoney));
//            // 积分兑换金额小于实际支付可抵扣金额
//            if (deductionPrice.compareTo(canDeductionPrice) <= 0) {
////                payPrice = payPrice.subtract(deductionPrice);
////                priceResponse.setSurplusIntegral(0);
//                priceResponse.setAvailableBalance(BigDecimal.ZERO);
//                priceResponse.setUsedIntegral(user.getIntegral());
//            } else {
//                deductionPrice = canDeductionPrice;
//                if (canDeductionPrice.compareTo(BigDecimal.ZERO) > 0) {
//                    int usedIntegral = canDeductionPrice.divide(new BigDecimal(integralDeductionMoney), 0, BigDecimal.ROUND_UP).intValue();
////                    priceResponse.setSurplusIntegral(user.getIntegral() - usedIntegral);
//                    priceResponse.setAvailableBalance(user.getNowMoney().subtract(canDeductionPrice));
//                    priceResponse.setUsedIntegral(usedIntegral);
//                }
//            }
//            payPrice = payPrice.subtract(deductionPrice);
//            priceResponse.setPayFee(payPrice.add(priceResponse.getFreightFee()));
//            priceResponse.setDeductionPrice(deductionPrice);
//            priceResponse.setIsUseIntegral(true);
//            priceResponse.setIntegralDeductionSwitch(true);
        }
        return priceResponse;
    }

    private List<CouponUser> computedPriceGetPlatOrderList(PreOrderInfoVo orderInfoVo) {
        Integer userId = userService.getUserId();
        // 获取平台可用优惠券列表
        List<Integer> merIdList = new ArrayList<>();
        List<Integer> proIdList = new ArrayList<>();
        List<Product> productList = new ArrayList<>();
        List<PreOrderInfoDetailVo> orderDetailInfoList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : orderInfoVo.getMerchantOrderVoList()) {
            merIdList.add(merchantOrderVo.getMerId());
            merchantOrderVo.getOrderInfoList().forEach(info -> {
                if (!proIdList.contains(info.getProductId())) {
                    Product product = productService.getById(info.getProductId());
                    productList.add(product);
                    proIdList.add(product.getId());
                }
            });
            orderDetailInfoList.addAll(merchantOrderVo.getOrderInfoList());
        }
        BigDecimal proTotalFee = orderInfoVo.getProTotalFee();
        List<Integer> proCategoryIdList = productList.stream().map(Product::getCategoryId).collect(Collectors.toList());
        List<Integer> secondParentIdList = productCategoryService.findParentIdByChildIds(proCategoryIdList);
        List<Integer> firstParentIdList = productCategoryService.findParentIdByChildIds(secondParentIdList);
        proCategoryIdList.addAll(secondParentIdList);
        proCategoryIdList.addAll(firstParentIdList);
        List<Integer> brandIdList = productList.stream().map(Product::getBrandId).collect(Collectors.toList());
        List<CouponUser> platCouponUserList = couponUserService.findManyPlatByUidAndMerIdAndMoneyAndProList(userId, proIdList, proCategoryIdList, merIdList, brandIdList, proTotalFee);

        BigDecimal remainingAmount = orderInfoVo.getProTotalFee().subtract(orderInfoVo.getMerCouponFee());
        for (CouponUser couponUser : platCouponUserList) {
            if (orderInfoVo.getPlatUserCouponId() > 0 && couponUser.getId().equals(orderInfoVo.getPlatUserCouponId())) {
                couponUser.setIsChecked(true);
                couponUser.setIsChoose(true);
            }
            // 判断是否可用
            if (remainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_UNIVERSAL)) {
                if (orderInfoVo.getProTotalFee().compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0 && remainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            Coupon coupon = couponService.getById(couponUser.getCouponId());
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> cpIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> cpIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0 && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT_CATEGORY)) {
                List<Integer> cidList = new ArrayList<>();
                Integer categoryId = Integer.valueOf(coupon.getLinkedData());
                ProductCategory category = productCategoryService.getById(categoryId);
                if (category.getLevel().equals(3)) {
                    cidList.add(categoryId);
                } else {
                    List<ProductCategory> productCategoryList = productCategoryService.findAllChildListByPid(category.getId(), category.getLevel());
                    if (category.getLevel().equals(1)) {
                        productCategoryList = productCategoryList.stream().filter(f -> f.getLevel().equals(3)).collect(Collectors.toList());
                    }
                    cidList.addAll(productCategoryList.stream().map(ProductCategory::getId).collect(Collectors.toList()));
                }
                List<Integer> pIdList = productList.stream().filter(f -> cidList.contains(f.getCategoryId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0 && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_BRAND)) {
                Integer brandId = Integer.valueOf(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> brandId.equals(f.getBrandId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0 && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_JOINT_MERCHANT)) {
                List<Integer> mpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> mpIdList.contains(f.getMerId())).map(Product::getId).collect(Collectors.toList());
                BigDecimal proPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proSubPrice = orderDetailInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum().toString())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0 && proSubPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    couponUser.setIsChoose(true);
                }
                continue;
            }
        }
        return platCouponUserList;
    }

    private List<ComputedMerchantOrderResponse> computedPriceGetMerOrderList(PreOrderInfoVo orderInfoVo) {
        Integer userId = userService.getUserId();
        List<ComputedMerchantOrderResponse> merOrderResponseList = orderInfoVo.getMerchantOrderVoList().stream().map(vo -> {
            ComputedMerchantOrderResponse merOrderResponse = new ComputedMerchantOrderResponse();
            merOrderResponse.setMerId(vo.getMerId());
            merOrderResponse.setUserCouponId(vo.getUserCouponId());
            merOrderResponse.setCouponFee(vo.getMerCouponFee());
            merOrderResponse.setFreightFee(vo.getFreightFee());
            // 查询适用的用户优惠券
            List<Integer> merProIdList = vo.getOrderInfoList().stream().map(PreOrderInfoDetailVo::getProductId).distinct().collect(Collectors.toList());
            BigDecimal merPrice = vo.getProTotalFee();
            BigDecimal merRemainingAmount = vo.getProTotalFee().subtract(orderInfoVo.getPlatCouponFee());
            List<CouponUser> merCouponUserList = couponUserService.findManyByUidAndMerIdAndMoneyAndProList(userId, vo.getMerId(), merProIdList, merPrice);
            for (int i = 0; i < merCouponUserList.size(); ) {
                CouponUser couponUser = merCouponUserList.get(i);
                if (vo.getUserCouponId() > 0 && couponUser.getId().equals(vo.getUserCouponId())) {
                    couponUser.setIsChecked(true);
                    couponUser.setIsChoose(true);
                    i++;
                    continue;
                }
                if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                    if (merRemainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                        couponUser.setIsChoose(true);
                    }
                    i++;
                    continue;
                }
                Coupon coupon = couponService.getById(couponUser.getCouponId());
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<PreOrderInfoDetailVo> detailVoList = vo.getOrderInfoList().stream().filter(f -> cpIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proPrice = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proRemainingAmount = detailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getPlatCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    merCouponUserList.remove(i);
                    continue;
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) >= 0) {
                    if (proRemainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                        couponUser.setIsChoose(true);
                    }
                }
                i++;
            }
            merOrderResponse.setMerCouponUserList(merCouponUserList);
            return merOrderResponse;
        }).collect(Collectors.toList());
        return merOrderResponseList;
    }

    /**
     * 视频号下单校验
     * 暂时只支持都买一种商品
     *
     * @param detailRequest 请求参数
     * @return 预下单结果
     */
    private PreMerchantOrderVo validatePreOrderVideo(PreOrderDetailRequest detailRequest) {
        logger.info("预下单下单校验 -》validatePreOrderVideo-》:{}", JSONObject.toJSONString(detailRequest));
        if (ObjectUtil.isNull(detailRequest.getProductId())) {
            throw new CrmebException("商品编号不能为空");
        }
        if (ObjectUtil.isNull(detailRequest.getAttrValueId())) {
            throw new CrmebException("商品规格属性值不能为空");
        }
        if (ObjectUtil.isNull(detailRequest.getProductNum()) || detailRequest.getProductNum() <= 0) {
            throw new CrmebException("购买数量必须大于0");
        }
        // 查询商品信息
        PayComponentProduct product = payComponentProductService.getById(detailRequest.getProductId());
        logger.info("预下单下单校验 -》product-》:{}", JSON.toJSONString(product));
        if (ObjectUtil.isNull(product)) {
            throw new CrmebException("商品信息不存在，请刷新后重新选择");
        }
        if (product.getIsDel()) {
            throw new CrmebException("商品已删除，请刷新后重新选择");
        }
        if (!product.getStatus().equals(5)) {
            throw new CrmebException("商品已下架，请刷新后重新选择");
        }
        if (product.getStock() < detailRequest.getProductNum()) {
            throw new CrmebException("商品库存不足，请刷新后重新选择");
        }
        // 查询商品规格属性值信息
        ProductAttrValue attrValue = productAttrValueService.getByIdAndProductIdAndType(detailRequest.getAttrValueId(), detailRequest.getProductId(), ProductConstants.PRODUCT_TYPE_COMPONENT);
        logger.info("预下单下单校验 -》attrValue:{}", JSON.toJSONString(attrValue));
        if (ObjectUtil.isNull(attrValue)) {
            throw new CrmebException("商品规格信息不存在，请刷新后重新选择");
        }
        if (attrValue.getStock() < detailRequest.getProductNum()) {
            throw new CrmebException("商品规格库存不足，请刷新后重新选择");
        }
        Merchant merchant = merchantService.getByIdException(product.getMerId());
        if (!merchant.getIsSwitch()) {
            throw new CrmebException("商户已关闭，请重新选择商品");
        }
        PreMerchantOrderVo merchantOrderVo = new PreMerchantOrderVo();
        merchantOrderVo.setMerId(merchant.getId());
        merchantOrderVo.setMerName(merchant.getName());
        merchantOrderVo.setFreightFee(BigDecimal.ZERO);
        merchantOrderVo.setCouponFee(BigDecimal.ZERO);
        merchantOrderVo.setUserCouponId(0);
        merchantOrderVo.setTakeTheirSwitch(merchant.getIsTakeTheir());
        PreOrderInfoDetailVo detailVo = new PreOrderInfoDetailVo();
        detailVo.setProductId(product.getId());
        detailVo.setProductName(product.getTitle());
        detailVo.setAttrValueId(attrValue.getId());
        detailVo.setSku(attrValue.getSku());
        detailVo.setPrice(attrValue.getPrice());
        detailVo.setPayPrice(attrValue.getPrice());
        detailVo.setPayNum(detailRequest.getProductNum());
        detailVo.setImage(StrUtil.isNotBlank(attrValue.getImage()) ? attrValue.getImage() : product.getHeadImg());
        detailVo.setVolume(attrValue.getVolume());
        detailVo.setWeight(attrValue.getWeight());
        detailVo.setTempId(product.getTempId());
        detailVo.setSubBrokerageType(product.getIsSub() ? 1 : 2);
        detailVo.setBrokerage(attrValue.getBrokerage());
        detailVo.setBrokerageTwo(attrValue.getBrokerageTwo());
//        PreOrderInfoDetailVo detailVo = new PreOrderInfoDetailVo();
//        detailVo.setProductId(product.getId());
//        detailVo.setProductName(product.getTitle());
//        detailVo.setAttrValueId(attrValue.getId());
//        detailVo.setSku(attrValue.getSku());
//        detailVo.setPrice(attrValue.getPrice());
//        detailVo.setPayNum(detailRequest.getProductNum());
//        detailVo.setImage(attrValue.getImage());
//        detailVo.setVolume(attrValue.getVolume());
//        detailVo.setWeight(attrValue.getWeight());
//        detailVo.setTempId(product.getTempId());
////        detailVo.setIsSub(product.getIsSub());
//        detailVo.setProductType(ProductConstants.PRODUCT_TYPE_COMPONENT);
        List<PreOrderInfoDetailVo> infoList = CollUtil.newArrayList();
        infoList.add(detailVo);
        merchantOrderVo.setOrderInfoList(infoList);
        logger.info("预下单下单校验-》merchantOrderVo:{}", JSON.toJSONString(merchantOrderVo));
        return merchantOrderVo;
    }

    /**
     * 获取优惠金额
     */
    @Override
    public void getCouponFee(PreOrderInfoVo orderInfoVo, List<OrderMerchantRequest> orderMerchantRequestList, Integer uid) {
        long count = orderMerchantRequestList.stream().filter(e -> e.getUserCouponId() > 0).count();
        if (count <= 0) {
            orderInfoVo.setCouponFee(BigDecimal.ZERO);
            return;
        }
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {
            throw new CrmebException("营销活动商品无法使用优惠券");
        }
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_VIDEO)) {
            throw new CrmebException("视频号商品无法使用优惠券");
        }
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            for (OrderMerchantRequest orderMerchantRequest : orderMerchantRequestList) {
                if (merchantOrderVo.getMerId().equals(orderMerchantRequest.getMerId()) && orderMerchantRequest.getUserCouponId() > 0) {
                    merchantOrderVo.setUserCouponId(orderMerchantRequest.getUserCouponId());
                }
            }
        }
        BigDecimal couponFee = BigDecimal.ZERO;
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            if (merchantOrderVo.getUserCouponId() <= 0) {
                continue;
            }
            BigDecimal merchantCouponFee = BigDecimal.ZERO;
            // 判断优惠券是否可以使用
            CouponUser couponUser = couponUserService.getById(merchantOrderVo.getUserCouponId());
            if (ObjectUtil.isNull(couponUser) || !couponUser.getUid().equals(uid)) {
                throw new CrmebException("优惠券领取记录不存在！");
            }
            if (!couponUser.getMerId().equals(merchantOrderVo.getMerId())) {
                throw new CrmebException("商家无此优惠券");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_USED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已使用！");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_LAPSED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已失效！");
            }
            //判断是否在使用时间内
            Date date = CrmebDateUtil.nowDateTime();
            if (couponUser.getStartTime().compareTo(date) > 0) {
                throw new CrmebException("此优惠券还未到达使用时间范围之内！");
            }
            if (date.compareTo(couponUser.getEndTime()) > 0) {
                throw new CrmebException("此优惠券已经失效了");
            }
            if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(orderInfoVo.getProTotalFee()) > 0) {
                throw new CrmebException("总金额小于优惠券最小使用金额");
            }
            //检测优惠券信息
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                // 商品券
                List<Integer> productIdList = merchantOrderVo.getOrderInfoList().stream().map(PreOrderInfoDetailVo::getProductId).collect(Collectors.toList());
                if (productIdList.size() < 1) {
                    throw new CrmebException("No item found product");
                }
                //设置优惠券所提供的集合
                List<CouponProduct> couponProductList = couponProductService.findByCid(couponUser.getCouponId());
                List<Integer> primaryKeyIdList = couponProductList.stream().map(CouponProduct::getPid).collect(Collectors.toList());
                //取两个集合的交集，如果是false则证明没有相同的值
                //oldList.retainAll(newList)返回值代表oldList是否保持原样，如果old和new完全相同，那old保持原样并返回false。
                //交集：listA.retainAll(listB) ——listA内容变为listA和listB都存在的对象；listB不变
                primaryKeyIdList.retainAll(productIdList);
                if (CollUtil.isEmpty(primaryKeyIdList)) {
                    throw new CrmebException("此券为商品券，请在购买相关产品后使用！");
                }
                List<PreOrderInfoDetailVo> infoDetailVoList = merchantOrderVo.getOrderInfoList().stream().filter(info -> primaryKeyIdList.contains(info.getProductId())).collect(Collectors.toList());
                if (CollUtil.isEmpty(infoDetailVoList)) {
                    throw new CrmebException("此券为商品券，请在购买相关产品后使用！");
                }
                BigDecimal proTotalPrice = infoDetailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(proTotalPrice) > 0) {
                    throw new CrmebException("总金额小于优惠券最低使用金额");
                }
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    merchantCouponFee = merchantCouponFee.add(new BigDecimal(couponUser.getMoney().toString()));
                } else {
                    merchantCouponFee = merchantCouponFee.add(proTotalPrice);
                }
                BigDecimal couponPrice = merchantCouponFee;
                if (infoDetailVoList.size() == 1) {
                    infoDetailVoList.get(0).setCouponPrice(couponPrice);
                } else {
                    for (int i = 0; i < infoDetailVoList.size(); i++) {
                        PreOrderInfoDetailVo detailVo = infoDetailVoList.get(i);
                        if (infoDetailVoList.size() == (i + 1)) {
                            detailVo.setCouponPrice(couponPrice);
                            break;
                        }
                        BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                        BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                        BigDecimal detailCouponFee = couponPrice.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                        couponPrice = couponPrice.subtract(detailCouponFee);
                        detailVo.setCouponPrice(detailCouponFee);
                    }
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                // 商家券
                List<PreOrderInfoDetailVo> infoDetailVoList = merchantOrderVo.getOrderInfoList();
                BigDecimal proTotalPrice = infoDetailVoList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(proTotalPrice) > 0) {
                    throw new CrmebException("总金额小于优惠券最低使用金额");
                }
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) > 0) {
                    merchantCouponFee = merchantCouponFee.add(new BigDecimal(couponUser.getMoney().toString()));
                } else {
                    merchantCouponFee = merchantCouponFee.add(proTotalPrice);
                }
                BigDecimal couponPrice = merchantCouponFee;
                if (infoDetailVoList.size() == 1) {
                    infoDetailVoList.get(0).setCouponPrice(couponPrice);
                } else {
                    for (int i = 0; i < infoDetailVoList.size(); i++) {
                        PreOrderInfoDetailVo detailVo = infoDetailVoList.get(i);
                        if (infoDetailVoList.size() == (i + 1)) {
                            detailVo.setCouponPrice(couponPrice);
                            break;
                        }
                        BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                        BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                        BigDecimal detailCouponFee = couponPrice.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                        couponPrice = couponPrice.subtract(detailCouponFee);
                        detailVo.setCouponPrice(detailCouponFee);
                    }
                }
            }
            couponFee = couponFee.add(merchantCouponFee);
            merchantOrderVo.setCouponFee(merchantCouponFee);
        }
        orderInfoVo.setCouponFee(couponFee);
    }

    /**
     * 获取优惠金额
     */
    private void getCouponFee_V1_3(PreOrderInfoVo orderInfoVo, Integer uid) {
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_SECKILL)) {
            throw new CrmebException("营销活动商品无法使用优惠券");
        }
        if (orderInfoVo.getType().equals(OrderConstants.ORDER_TYPE_VIDEO)) {
            throw new CrmebException("视频号商品无法使用优惠券");
        }
        orderInfoVo.getMerchantOrderVoList().forEach(merchantOrder -> {
            merchantOrder.setCouponFee(BigDecimal.ZERO);
            merchantOrder.setMerCouponFee(BigDecimal.ZERO);
            merchantOrder.getOrderInfoList().forEach(info -> {
                info.setMerCouponPrice(BigDecimal.ZERO);
                info.setPlatCouponPrice(BigDecimal.ZERO);
            });
        });
        List<PreMerchantOrderVo> merchantOrderVoList = orderInfoVo.getMerchantOrderVoList();
        long count = merchantOrderVoList.stream().filter(e -> e.getUserCouponId() > 0).count();
        if (count <= 0 && orderInfoVo.getPlatUserCouponId() <= 0) {
            orderInfoVo.setCouponFee(BigDecimal.ZERO);
            return;
        }
        BigDecimal couponFee = BigDecimal.ZERO;
        Date date = CrmebDateUtil.nowDateTime();
        List<Integer> proIdList = new ArrayList<>();
        for (PreMerchantOrderVo merchantOrderVo : merchantOrderVoList) {
            List<Integer> productIdList = merchantOrderVo.getOrderInfoList().stream().map(PreOrderInfoDetailVo::getProductId).collect(Collectors.toList());
            proIdList.addAll(productIdList);
            if (merchantOrderVo.getUserCouponId() <= 0) {
                merchantOrderVo.setCouponFee(BigDecimal.ZERO);
                merchantOrderVo.setMerCouponFee(BigDecimal.ZERO);
                continue;
            }
            BigDecimal merchantCouponFee = BigDecimal.ZERO;
            // 判断优惠券是否可以使用
            CouponUser couponUser = couponUserService.getById(merchantOrderVo.getUserCouponId());
            if (ObjectUtil.isNull(couponUser) || !couponUser.getUid().equals(uid)) {
                throw new CrmebException("优惠券领取记录不存在！");
            }
            if (!couponUser.getMerId().equals(merchantOrderVo.getMerId())) {
                throw new CrmebException("商家无此优惠券");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_USED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已使用！");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_LAPSED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已失效！");
            }
            //判断是否在使用时间内
            if (couponUser.getStartTime().compareTo(date) > 0) {
                throw new CrmebException("此优惠券还未到达使用时间范围之内！");
            }
            if (date.compareTo(couponUser.getEndTime()) > 0) {
                throw new CrmebException("此优惠券已经失效了");
            }
            if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(merchantOrderVo.getProTotalFee()) > 0) {
//                throw new CrmebException("总金额小于优惠券最小使用金额");
                throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
            }

            //检测优惠券信息
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                // 商品券
                //设置优惠券所提供的集合
                List<CouponProduct> couponProductList = couponProductService.findByCid(couponUser.getCouponId());
                List<Integer> primaryKeyIdList = couponProductList.stream().map(CouponProduct::getPid).collect(Collectors.toList());
                //取两个集合的交集，如果是false则证明没有相同的值
                //oldList.retainAll(newList)返回值代表oldList是否保持原样，如果old和new完全相同，那old保持原样并返回false。
                //交集：listA.retainAll(listB) ——listA内容变为listA和listB都存在的对象；listB不变
                primaryKeyIdList.retainAll(productIdList);
                if (CollUtil.isEmpty(primaryKeyIdList)) {
                    throw new CrmebException("此券为商品券，请在购买相关产品后使用！");
                }
                List<PreOrderInfoDetailVo> infoDetailVoList = merchantOrderVo.getOrderInfoList().stream().filter(info -> primaryKeyIdList.contains(info.getProductId())).collect(Collectors.toList());
                if (CollUtil.isEmpty(infoDetailVoList)) {
                    throw new CrmebException("此券为商品券，请在购买相关产品后使用！");
                }
                BigDecimal proTotalPrice = infoDetailVoList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(proTotalPrice) > 0) {
//                    throw new CrmebException("总金额小于优惠券最低使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                merchantCouponFee = merchantCouponFee.add(new BigDecimal(couponUser.getMoney().toString()));
                BigDecimal couponPrice = merchantCouponFee;
                if (infoDetailVoList.size() == 1) {
                    infoDetailVoList.get(0).setMerCouponPrice(couponPrice);
                } else {
                    for (int i = 0; i < infoDetailVoList.size(); i++) {
                        PreOrderInfoDetailVo detailVo = infoDetailVoList.get(i);
                        if (infoDetailVoList.size() == (i + 1)) {
                            detailVo.setMerCouponPrice(couponPrice);
                            break;
                        }
                        BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                        BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                        BigDecimal detailCouponFee = couponPrice.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                        couponPrice = couponPrice.subtract(detailCouponFee);
                        detailVo.setMerCouponPrice(detailCouponFee);
                    }
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_MERCHANT)) {
                // 商家券
                List<PreOrderInfoDetailVo> infoDetailVoList = merchantOrderVo.getOrderInfoList();
                BigDecimal proTotalPrice = infoDetailVoList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (new BigDecimal(couponUser.getMinPrice().toString()).compareTo(proTotalPrice) > 0) {
//                    throw new CrmebException("总金额小于优惠券最低使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                merchantCouponFee = merchantCouponFee.add(new BigDecimal(couponUser.getMoney().toString()));
                BigDecimal couponPrice = merchantCouponFee;
                if (infoDetailVoList.size() == 1) {
                    infoDetailVoList.get(0).setMerCouponPrice(couponPrice);
                } else {
                    for (int i = 0; i < infoDetailVoList.size(); i++) {
                        PreOrderInfoDetailVo detailVo = infoDetailVoList.get(i);
                        if (infoDetailVoList.size() == (i + 1)) {
                            detailVo.setMerCouponPrice(couponPrice);
                            break;
                        }
                        BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                        BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                        BigDecimal detailCouponFee = couponPrice.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                        couponPrice = couponPrice.subtract(detailCouponFee);
                        detailVo.setMerCouponPrice(detailCouponFee);
                    }
                }
            }
            couponFee = couponFee.add(merchantCouponFee);
            merchantOrderVo.setCouponFee(merchantCouponFee);
            merchantOrderVo.setMerCouponFee(merchantCouponFee);
        }
        orderInfoVo.setMerCouponFee(couponFee);
        // 计算平台优惠券
        if (orderInfoVo.getPlatUserCouponId() > 0) {

            CouponUser couponUser = couponUserService.getById(orderInfoVo.getPlatUserCouponId());
            if (ObjectUtil.isNull(couponUser) || !couponUser.getUid().equals(uid)) {
                throw new CrmebException("优惠券领取记录不存在！");
            }
            if (!couponUser.getMerId().equals(0)) {
                throw new CrmebException("平台无此优惠券");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_USED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已使用！");
            }
            if (CouponConstants.STORE_COUPON_USER_STATUS_LAPSED.equals(couponUser.getStatus())) {
                throw new CrmebException("此优惠券已失效！");
            }
            //判断是否在使用时间内
            if (couponUser.getStartTime().compareTo(date) > 0) {
                throw new CrmebException("此优惠券还未到达使用时间范围之内！");
            }
            if (date.compareTo(couponUser.getEndTime()) > 0) {
                throw new CrmebException("此优惠券已经失效了");
            }

            Coupon coupon = couponService.getById(couponUser.getCouponId());
            List<PreOrderInfoDetailVo> orderInfoList = new ArrayList<>();
            merchantOrderVoList.forEach(m -> {
                orderInfoList.addAll(m.getOrderInfoList());
            });
            BigDecimal platCouponPrice = new BigDecimal(couponUser.getMoney().toString());
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_UNIVERSAL)) {
                BigDecimal remainingAmount = orderInfoVo.getProTotalFee().subtract(orderInfoVo.getMerCouponFee());
                if (orderInfoVo.getProTotalFee().compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
//                    throw new CrmebException("总金额小于优惠券最小使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (remainingAmount.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                BigDecimal proTotalPrice = orderInfoList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                for (int i = 0; i < orderInfoList.size(); i++) {
                    PreOrderInfoDetailVo detailVo = orderInfoList.get(i);
                    if (orderInfoList.size() == (i + 1)) {
                        detailVo.setPlatCouponPrice(platCouponPrice);
                        detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                        break;
                    }
                    BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                    BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                    BigDecimal detailCouponFee = new BigDecimal(couponUser.getMoney().toString()).multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    platCouponPrice = platCouponPrice.subtract(detailCouponFee);
                    detailVo.setPlatCouponPrice(detailCouponFee);
                    detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT)) {
                List<Integer> cpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                // 平台优惠券计算金额 = （商品金额 - 商户优惠金额） * 购买数量
                List<PreOrderInfoDetailVo> infoDetailList = orderInfoList.stream().filter(f -> cpIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proTotalPrice = infoDetailList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proPrice = infoDetailList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
//                    throw new CrmebException("总金额小于优惠券最小使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                for (int i = 0; i < infoDetailList.size(); i++) {
                    PreOrderInfoDetailVo detailVo = infoDetailList.get(i);
                    if (orderInfoList.size() == (i + 1)) {
                        detailVo.setPlatCouponPrice(platCouponPrice);
                        detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                        break;
                    }
                    BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                    BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                    BigDecimal detailCouponFee = new BigDecimal(couponUser.getMoney().toString()).multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    platCouponPrice = platCouponPrice.subtract(detailCouponFee);
                    detailVo.setPlatCouponPrice(detailCouponFee);
                    detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                }
            }
            proIdList = proIdList.stream().distinct().collect(Collectors.toList());
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_PRODUCT_CATEGORY)) {
                List<Integer> cidList = new ArrayList<>();
                Integer categoryId = Integer.valueOf(coupon.getLinkedData());
                ProductCategory category = productCategoryService.getById(categoryId);
                if (category.getLevel().equals(3)) {
                    cidList.add(categoryId);
                } else {
                    List<ProductCategory> productCategoryList = productCategoryService.findAllChildListByPid(category.getId(), category.getLevel());
                    if (category.getLevel().equals(1)) {
                        productCategoryList = productCategoryList.stream().filter(f -> f.getLevel().equals(3)).collect(Collectors.toList());
                    }
                    cidList.addAll(productCategoryList.stream().map(ProductCategory::getId).collect(Collectors.toList()));
                }
                List<Product> productList = productService.findByIds(proIdList);
                List<Integer> pIdList = productList.stream().filter(f -> cidList.contains(f.getCategoryId())).map(Product::getId).collect(Collectors.toList());
                List<PreOrderInfoDetailVo> infoDetailList = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proTotalPrice = infoDetailList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proPrice = infoDetailList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
//                    throw new CrmebException("总金额小于优惠券最小使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                for (int i = 0; i < infoDetailList.size(); i++) {
                    PreOrderInfoDetailVo detailVo = infoDetailList.get(i);
                    if (orderInfoList.size() == (i + 1)) {
                        detailVo.setPlatCouponPrice(platCouponPrice);
                        detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                        break;
                    }
                    BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                    BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                    BigDecimal detailCouponFee = new BigDecimal(couponUser.getMoney().toString()).multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    platCouponPrice = platCouponPrice.subtract(detailCouponFee);
                    detailVo.setPlatCouponPrice(detailCouponFee);
                    detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_BRAND)) {
                Integer brandId = Integer.valueOf(coupon.getLinkedData());
                List<Product> productList = productService.findByIds(proIdList);
                List<Integer> pIdList = productList.stream().filter(f -> brandId.equals(f.getBrandId())).map(Product::getId).collect(Collectors.toList());
                List<PreOrderInfoDetailVo> infoDetailList = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proTotalPrice = infoDetailList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proPrice = infoDetailList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
//                    throw new CrmebException("总金额小于优惠券最小使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                for (int i = 0; i < infoDetailList.size(); i++) {
                    PreOrderInfoDetailVo detailVo = infoDetailList.get(i);
                    if (orderInfoList.size() == (i + 1)) {
                        detailVo.setPlatCouponPrice(platCouponPrice);
                        detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                        break;
                    }
                    BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                    BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                    BigDecimal detailCouponFee = new BigDecimal(couponUser.getMoney().toString()).multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    platCouponPrice = platCouponPrice.subtract(detailCouponFee);
                    detailVo.setPlatCouponPrice(detailCouponFee);
                    detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                }
            }
            if (couponUser.getCategory().equals(CouponConstants.COUPON_CATEGORY_JOINT_MERCHANT)) {
                List<Product> productList = productService.findByIds(proIdList);
                List<Integer> mpIdList = CrmebUtil.stringToArray(coupon.getLinkedData());
                List<Integer> pIdList = productList.stream().filter(f -> mpIdList.contains(f.getMerId())).map(Product::getId).collect(Collectors.toList());
                List<PreOrderInfoDetailVo> infoDetailList = orderInfoList.stream().filter(f -> pIdList.contains(f.getProductId())).collect(Collectors.toList());
                BigDecimal proTotalPrice = infoDetailList.stream().map(i -> i.getPrice().multiply(new BigDecimal(i.getPayNum()))).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal proPrice = infoDetailList.stream().map(e -> e.getPrice().multiply(new BigDecimal(e.getPayNum())).subtract(e.getMerCouponPrice())).reduce(BigDecimal.ZERO, BigDecimal::add);
                if (proTotalPrice.compareTo(new BigDecimal(couponUser.getMinPrice().toString())) < 0) {
//                    throw new CrmebException("总金额小于优惠券最小使用金额");
                    throw new CrmebException("请合理搭配优惠组合，当前优惠券不满足使用条件");
                }
                if (proPrice.compareTo(new BigDecimal(couponUser.getMoney().toString())) <= 0) {
                    throw new CrmebException(StrUtil.format("{}优惠券不适用，请重新选择优惠券", couponUser.getName()));
                }
                for (int i = 0; i < infoDetailList.size(); i++) {
                    PreOrderInfoDetailVo detailVo = infoDetailList.get(i);
                    if (orderInfoList.size() == (i + 1)) {
                        detailVo.setPlatCouponPrice(platCouponPrice);
                        detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                        break;
                    }
                    BigDecimal detailPrice = detailVo.getPrice().multiply(new BigDecimal(detailVo.getPayNum()));
                    BigDecimal ratio = detailPrice.divide(proTotalPrice, 10, BigDecimal.ROUND_HALF_UP);
                    BigDecimal detailCouponFee = new BigDecimal(couponUser.getMoney().toString()).multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    platCouponPrice = platCouponPrice.subtract(detailCouponFee);
                    detailVo.setPlatCouponPrice(detailCouponFee);
                    detailVo.setCouponPrice(detailVo.getMerCouponPrice().add(detailVo.getPlatCouponPrice()));
                }
            }
            BigDecimal platCouponFee = new BigDecimal(couponUser.getMoney().toString());
            orderInfoVo.setPlatCouponFee(platCouponFee);
        }
        orderInfoVo.setCouponFee(orderInfoVo.getPlatCouponFee().add(orderInfoVo.getMerCouponFee()));
    }

    @Override
    public List<OrderFrontDataResponse> listMyBuyOrder(UserOrderListRequest userOrderListRequest, PageParamRequest pageRequest) {
        Integer userId = userService.getUserIdException();
        userOrderListRequest.setBuyerId(userId);
        userOrderListRequest.setOwnerId(null);
        List<OrderFrontDataResponse> responseList = CollUtil.newArrayList();
        List<Order> orders = orderService.listUserOrder(userOrderListRequest, pageRequest);
        List<Integer> merIdList = orders.stream().map(Order::getMerId).filter(i -> i > 0).distinct().collect(Collectors.toList());

        Map<Integer, Merchant> merchantMap = Maps.newHashMap();
        if (CollUtil.isNotEmpty(merIdList)) {
            merchantMap = merchantService.getMerIdMapByIdList(merIdList);
        }
        for (Order order : orders) {
            OrderFrontDataResponse response = new OrderFrontDataResponse();
            BeanUtils.copyProperties(order, response);
            // 订单详情对象列表
            List<OrderDetail> orderDetailList = orderDetailService.getByOrderNo(order.getOrderNo());
            List<OrderInfoFrontDataResponse> infoResponseList = CollUtil.newArrayList();
            orderDetailList.forEach(e -> {
                OrderInfoFrontDataResponse orderInfoResponse = new OrderInfoFrontDataResponse();
                BeanUtils.copyProperties(e, orderInfoResponse);
                infoResponseList.add(orderInfoResponse);
            });
            response.setOrderInfoList(infoResponseList);

            if (order.getMerId() > 0) {
                response.setMerName(merchantMap.get(order.getMerId()).getName());
            }
            responseList.add(response);
        }
        return responseList;
    }

    @Override
    public List<OrderFrontDataResponse> listMySaleOrder(UserOrderListRequest userOrderListRequest, PageParamRequest pageRequest) {
        Integer userId = userService.getUserIdException();
        userOrderListRequest.setBuyerId(null);
        userOrderListRequest.setOwnerId(userId);
        List<OrderFrontDataResponse> responseList = CollUtil.newArrayList();
        List<Order> orders = orderService.listUserOrder(userOrderListRequest, pageRequest);

        List<Integer> merIdList = orders.stream().map(Order::getMerId).filter(i -> i > 0).distinct().collect(Collectors.toList());

        Map<Integer, Merchant> merchantMap = Maps.newHashMap();
        if (CollUtil.isNotEmpty(merIdList)) {
            merchantMap = merchantService.getMerIdMapByIdList(merIdList);
        }

        for (Order order : orders) {
            OrderFrontDataResponse response = new OrderFrontDataResponse();
            BeanUtils.copyProperties(order, response);

            // 订单详情对象列表
            List<OrderDetail> orderDetailList = orderDetailService.getByOrderNo(order.getOrderNo());
            List<OrderInfoFrontDataResponse> infoResponseList = CollUtil.newArrayList();
            orderDetailList.forEach(e -> {
                OrderInfoFrontDataResponse orderInfoResponse = new OrderInfoFrontDataResponse();
                BeanUtils.copyProperties(e, orderInfoResponse);
                infoResponseList.add(orderInfoResponse);
            });
            response.setOrderInfoList(infoResponseList);

            if (order.getMerId() > 0) {
                response.setMerName(merchantMap.get(order.getMerId()).getName());
            }
            responseList.add(response);
        }

        return responseList;
    }

    @Override
    public UserOrderQuotaLimitVO userOrderQuotaLimit(Integer productId) {
        Integer userId = userService.getUserId();
        return new UserOrderQuotaLimitVO(1, BigDecimal.ZERO, BigDecimal.ZERO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderAddressModify(OrderAddressModifyReq req) {
        Boolean result = orderAddressService.updateAddressByAddressId(req.convert());
        if (BooleanUtil.isFalse(result)) {
            throw new CrmebException("修改订单地址失败");
        }
    }

    @Override
    public LogisticsResultVo getOrderDeliveryInfo(Integer deliveryId) {
        return orderService.getLogisticsInfo(deliveryId);
    }

    @Override
    public LeaseOrderProlongCurrentRentVO leaseOrderProlongCurrentRent(String orderNo) {
        final LocalDateTime now = LocalDateTime.now();
        OrderRentSharedRecord orderRentSharedRecord = orderRentRecordService.getActiveRentRecordByOrderNo(orderNo);
        // 直接查询订单明细
        final OrderDetail oneByOrderNo = orderDetailService.getOneByOrderNo(orderNo);
        final Product product = productService.getById(oneByOrderNo.getProductId());
        LeaseOrderProlongCurrentRentVO leaseOrderProlongCurrentRentVO = new LeaseOrderProlongCurrentRentVO();
        leaseOrderProlongCurrentRentVO.setOverDays(0L);
        leaseOrderProlongCurrentRentVO.setTimeoutFee(BigDecimal.ZERO);
        leaseOrderProlongCurrentRentVO.setRentEndDate(orderRentSharedRecord.getRentReturnDate().toLocalDate());
        // 查询已经支付了多少钱
        List<OrderRentPaymentRecord> autoDeduction = orderRentPaymentService.getByOrderNo(orderNo, OrderRentPaymentRecord.PayTradeType.OVERDUE_DEDUCTION_AUTO, OrderRentPaymentRecord.PayType.BALANCE_DEDUCTION);
        BigDecimal hasDeduction = autoDeduction.stream().map(OrderRentPaymentRecord::getIntegralAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        leaseOrderProlongCurrentRentVO.setIntegralAmount(hasDeduction);
        // 如果当前时间，在过期时间之后，则表示是已经过期，就需要计算需要多少钱
        if (now.isAfter(orderRentSharedRecord.getRentReturnDate()) && now.isBefore(orderRentSharedRecord.getExpiredEndDate())) {
            // 超期罚款金额
            long days = Duration.between(orderRentSharedRecord.getRentReturnDate(), now).toDays();
            leaseOrderProlongCurrentRentVO.setOverDays(days);
            // 超期罚金
            BigDecimal timeoutFee = product.getRentPrice().multiply(BigDecimal.valueOf(days));
            leaseOrderProlongCurrentRentVO.setTimeoutFee(timeoutFee);
        }
        leaseOrderProlongCurrentRentVO.setProductPrice(product.getPrice());
        leaseOrderProlongCurrentRentVO.setPayAmount(leaseOrderProlongCurrentRentVO.getTimeoutFee().subtract(hasDeduction));
        return leaseOrderProlongCurrentRentVO;
    }

    @Override
    public LeaseOrderProlongCalculateVO leaseOrderProlongCalculate(LeaseProlongCalculateReq req) {
        final LeaseOrderProlongCalculateVO vo = new LeaseOrderProlongCalculateVO();
        vo.setFreight(BigDecimal.ZERO);
        vo.setDeductions(BigDecimal.ZERO);
        final LocalDateTime now = LocalDateTime.now();
        // 直接查询订单明细
        final OrderDetail oneByOrderNo = orderDetailService.getOneByOrderNo(req.getOrderNo());
        final Product product = productService.getById(oneByOrderNo.getProductId());
        // 支付金额加上当前商品总金额
        vo.setTotalAmount(product.getRentPrice().multiply(BigDecimal.valueOf(req.getDays())));
        // 查询已经支付了多少钱
        List<OrderRentPaymentRecord> autoDeduction = orderRentPaymentService.getByOrderNo(req.getOrderNo(), OrderRentPaymentRecord.PayTradeType.OVERDUE_DEDUCTION_AUTO, OrderRentPaymentRecord.PayType.BALANCE_DEDUCTION);
        BigDecimal hasDeduction = autoDeduction.stream().map(OrderRentPaymentRecord::getIntegralAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        vo.setDeductionsAuto(hasDeduction);
        // 当前激活的承享的订单超时时间
        OrderRentSharedRecord orderRentSharedRecord = orderRentRecordService.getActiveRentRecordByOrderNo(req.getOrderNo());
        if (Objects.isNull(orderRentSharedRecord)) {
            throw new CrmebException("当前承享未开始，不能操作延长");
        }
        if (!(Objects.equals(orderRentSharedRecord.getStatus(), OrderRentSharedRecord.RentRecordStatus.RETURN.getCode()) || Objects.equals(orderRentSharedRecord.getStatus(), OrderRentSharedRecord.RentRecordStatus.EXPIRE.getCode()))) {
            throw new CrmebException("当前承享状态不在归还期或者宽容期，不能操作延长");
        }
        // 订单超时之后，允许一天寄回时间
        final LocalDateTime receivedDate = orderRentSharedRecord.getRentReturnDate();
        // 订单超时宽容期限
        final LocalDateTime rentExpiredGraceDate = orderRentSharedRecord.getExpiredEndDate();
        // 如果当前时间，在过期时间之后，则表示是已经过期，就需要计算需要多少钱
        if (now.isAfter(receivedDate) && now.isBefore(rentExpiredGraceDate)) {
            // 超期罚款金额
            long days = Duration.between(receivedDate, now).toDays();
            vo.setTimeoutFee(product.getRentPrice().multiply(BigDecimal.valueOf(days)));
        } else {
            vo.setTimeoutFee(BigDecimal.ZERO);
        }
        // 是否使用雅识抵扣
        if (req.getIsUseIntegral()) {
            User user = userService.getInfo();
            BigDecimal amount = vo.calculationPayAmount();
            UseAvailableResultDTO availableAmount = fundManager.getAvailableAmount(user, amount.subtract(BigDecimal.valueOf(0.01)), FundAccountType.CRAB_ROE.name());
            vo.setDeductions(availableAmount.getRealAmount());
        }
        vo.setPayAmount(vo.calculationPayAmount());

        // 记录支付订单
        return vo;
    }

    @Override
    public RentOrderProlongCreateVO leaseOrderProlongCreate(LeaseProlongCalculateReq req) {
        // 直接查询订单明细
        final Order order = orderService.getByOrderNo(req.getOrderNo());
        final OrderDetail oneByOrderNo = orderDetailService.getOneByOrderNo(req.getOrderNo());
        if (!Objects.equals(OrderType.RENT.code(), order.getType())) {
            throw new CrmebException("订单不是承享订单，不可延长");
        }
        // 当前承享记录
        OrderRentSharedRecord activeRentRecord = orderRentRecordService.getActiveRentRecordByOrderNo(req.getOrderNo());
        if (Objects.isNull(activeRentRecord)) {
            throw new CrmebException("当前承享未开始，不能操作延长");
        }
        if (!(Objects.equals(activeRentRecord.getStatus(), OrderRentSharedRecord.RentRecordStatus.RETURN.getCode()) || Objects.equals(activeRentRecord.getStatus(), OrderRentSharedRecord.RentRecordStatus.EXPIRE.getCode()))) {
            throw new CrmebException("当前承享未在可延长状态，不能操作延长");
        }
        // 创建承享订单的承享记录
        OrderRentRecordCreateDTO orderRentRecordCreateDTO = new OrderRentRecordCreateDTO();
        orderRentRecordCreateDTO.setOrderNo(req.getOrderNo());
        orderRentRecordCreateDTO.setOrderDetailId(oneByOrderNo.getId());
        orderRentRecordCreateDTO.setUid(order.getUid());
        orderRentRecordCreateDTO.setRentDays(req.getDays());
        orderRentRecordCreateDTO.setType(OrderRentSharedRecord.RentType.RENEWAL);
        // 创建支付订单
        final LeaseOrderProlongCalculateVO calculateResult = this.leaseOrderProlongCalculate(req);
        final OrderRentOverduePaymentRecordCreateDTO rentPayOrder = OrderRentOverduePaymentRecordCreateDTO.buildRentPayOrder(req, order, calculateResult);
        // 构建返回参数
        final RentOrderProlongCreateVO rentOrderProlongCreateVO = new RentOrderProlongCreateVO();
        Boolean execute = transactionTemplate.execute(e -> {
            String rentCode = orderRentRecordService.createOrderRentRecord(orderRentRecordCreateDTO);
            if (StringUtils.isEmpty(rentCode)) {
                e.setRollbackOnly();
                return Boolean.FALSE;
            }
            rentPayOrder.setRentRecordCode(rentCode);
            String create = orderRentPaymentService.create(rentPayOrder);
            if (StringUtils.isEmpty(create)) {
                e.setRollbackOnly();
                return Boolean.FALSE;
            } else {
                rentOrderProlongCreateVO.setOrderNo(create);
            }
            return Boolean.TRUE;
        });
        if (BooleanUtil.isFalse(execute)) {
            throw new CrmebException("承享订单续期失败");
        }
        return rentOrderProlongCreateVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ReserveRentReturnPickUpVO reserveRentReturnPickUp(ReserveRentReturnPickUpReq req) {
        // 获取预约时间编码对应的时间
        PickupAppointmentAvailableTime availableTime = PickupAppointmentAvailableTime.getByCode(req.getPickupCode());
        final LocalDateTime pickupTime = req.getPickupDate().atTime(availableTime.getStart());
        // 这里获取的是 SH 单号，换成 平台单号
        Order order = orderService.getByOrderNo(req.getOrderNo());
        final String platOrderNo = order.getPlatOrderNo();
        // 对订单创建地址信息
        // 1. 发出地址, 用户地址
        UserAddress userAddress = userAddressService.getById(req.getAddressId());
        Assert.notNull(userAddress, "用户地址异常");
        orderAddressService.insertAddress(platOrderNo, OrderAddressType.SHIPPER, ExpressOrderType.RENT_RETURN, OrderAddressCreateDTO.buildByUserAddress(userAddress));
        // 2. 收货地址 - 商户地址
        Merchant merchant = merchantService.getById(order.getMerId());
        Assert.notNull(merchant, "商户信息不存在");
        orderAddressService.insertAddress(platOrderNo, OrderAddressType.RECEIVER, ExpressOrderType.RENT_RETURN, OrderAddressCreateDTO.buildByMerchantAddress(merchant));
        // 对订单，创建一个物流记录，类型是承享订单归还的取件
        CreateExpressOrderDTO expressOrder = orderService.createExpressOrder(platOrderNo, pickupTime, ExpressOrderPayMethodType.RECEIVER_PAY, ExpressOrderType.RENT_RETURN);
        // 承享订单状态修改， 修改成待归还
        orderService.updateToBeReturned(platOrderNo, RentOrderReturnType.PICKUP);
        ReserveRentReturnPickUpVO pickUpVO = new ReserveRentReturnPickUpVO();
        pickUpVO.setPickupCode(expressOrder.getPickupCode());
        pickUpVO.setPickUpDate(req.getPickupDate());
        pickUpVO.setStartTime(availableTime.getStart());
        pickUpVO.setEndTime(availableTime.getEnd());
        pickUpVO.setUserAddressVO(UserAddressVO.buildByUserAddress(userAddress));
        pickUpVO.setDateDesc(DateConverter.convertDate(req.getPickupDate()));
        pickUpVO.setDeliveryName(ExpressType.SF.getName());
        return pickUpVO;
    }

    @Override
    public void reserveRentReturnInStore(ReserveRentReturnInStoreReq req) {
        // 获取预约时间编码对应的时间
        PickupAppointmentAvailableTime availableTime = PickupAppointmentAvailableTime.getByCode(req.getPickupCode());
        // 这里获取的是 SH 单号，换成 平台单号
        Order order = orderService.getByOrderNo(req.getOrderNo());
        final String platOrderNo = order.getPlatOrderNo();
        // 创建一个预约记录
        merchantOrderAppointmentService.createMerchantOrderAppointment(platOrderNo, order.getMerId(), order.getUid(), req.getPickupDate(), availableTime.getStart(), availableTime.getEnd(), MerOrderApptType.RENT_RETURN);
        // 承享订单状态修改， 修改成待归还
        orderService.updateToBeReturned(platOrderNo, RentOrderReturnType.STORE);
    }

    @Override
    public void reserveRentReturnPickupCancel(String orderNo) {
        // 这里获取的是 SH 单号，换成 平台单号
        Order order = orderService.getByOrderNo(orderNo);
        // 获取到OrderInvoice, 然后取消订单
        orderInvoiceService.cnacelExpressOrder(order.getPlatOrderNo(), ExpressOrderType.RENT_RETURN);
    }

    @Override
    public void reserveRentReturnPickupModifyTime(ReserveRentReturnInStoreModifyTimeReq req) {
        // 获取预约时间编码对应的时间
        PickupAppointmentAvailableTime availableTime = PickupAppointmentAvailableTime.getByCode(req.getPickupCode());
        // 这里获取的是 SH 单号，换成 平台单号
        Order order = orderService.getByOrderNo(req.getOrderNo());
        // 获取到OrderInvoice更新上门时间
        orderInvoiceService.updateExpressOrderPickupTime(order.getPlatOrderNo(), ExpressOrderType.RENT_RETURN, req.getPickupDate().atTime(availableTime.getStart()));
    }

    @Override
    public void reserveRentReturnInStoreCancel(ReserveRentReturnInStoreReq req) {
        Order order = orderService.getByOrderNo(req.getOrderNo());
        final String platOrderNo = order.getPlatOrderNo();
        // 取消预约
        merchantOrderAppointmentService.cancelMerchantOrderAppointment(platOrderNo, MerOrderApptType.RENT_RETURN);
        // 承享订单状态修改， 修改成待归还
        // orderService.updateToBeReturned(platOrderNo, RentOrderReturnType.STORE);
    }

    @Override
    public void reserveRentReturnInStoreModifyTime(ReserveRentReturnInStoreReq req) {
        // 获取预约时间编码对应的时间
        PickupAppointmentAvailableTime availableTime = PickupAppointmentAvailableTime.getByCode(req.getPickupCode());
        // 这里获取的是 SH 单号，换成 平台单号
        Order order = orderService.getByOrderNo(req.getOrderNo());
        final String platOrderNo = order.getPlatOrderNo();
        // 更新一个预约记录
        merchantOrderAppointmentService.modifyMerchantOrderAppointmentTime(platOrderNo, MerOrderApptType.RENT_RETURN, req.getPickupDate(), availableTime.getStart(), availableTime.getEnd());
    }

    @Override
    public RentOrderProlongPaymentResultVO leaseOrderProlongPaymentResult(String payOrderNo) {
        OrderRentPaymentRecord byPayOrderNo = orderRentPaymentService.getByPayOrderNo(payOrderNo);
        RentOrderProlongPaymentResultVO rentOrderProlongPaymentResultVO = new RentOrderProlongPaymentResultVO();
        rentOrderProlongPaymentResultVO.setUid(byPayOrderNo.getUid());
        rentOrderProlongPaymentResultVO.setPayChannel(byPayOrderNo.getPayChannel());
        rentOrderProlongPaymentResultVO.setOutTradeNo(byPayOrderNo.getOutTradeNo());
        rentOrderProlongPaymentResultVO.setCreateTime(byPayOrderNo.getCreateTime());
        rentOrderProlongPaymentResultVO.setOrderNo(byPayOrderNo.getPayOrderNo());
        rentOrderProlongPaymentResultVO.setPayTime(byPayOrderNo.getPayTime());
        rentOrderProlongPaymentResultVO.setPayType(byPayOrderNo.getPayType());
        rentOrderProlongPaymentResultVO.setPrice(byPayOrderNo.getPayAmount());
        rentOrderProlongPaymentResultVO.setPaid(byPayOrderNo.getPaidStatus());
        return rentOrderProlongPaymentResultVO;
    }
}
